Jason Beaver: Good afternoon.
Welcome to Understanding the Document Interaction Controller.
My name is Jason Beaver.
I'm an engineer on the iPhone Frameworks Team and I'll be joined in a little bit by Luke who is also on the team, to go through a few demos.
So, what are we going to cover today?
Well, there's 5 basic things.
We're going to talk a little bit about what documents are like on the iPhone OS or iOS now.
We're going to talk about how you register your applications as owners or editors of a particular type of document.
Once you've registered for a document, we're going to talk about what you need to do to actually open the document when you're asked about the operating system.
And if your application allows users to interact with documents, as an example of this, consider the mail application where attachments can get mailed to the user and the user can click on those and open them and preview and things like that.
We're going to talk about a new class called the UIDocumentInteractionController that automates a lot of this behavior for you.
And finally, we're going to spend a little time covering the QuickLook framework.
The QuickLook framework has been around on the desktop for quite awhile.
It's new to iOS and DocumentInteractionController uses it to handle preview functionality, but it's public and you can use it yourself, if the way that the DocumentInteractionController exposes it is not appropriate in your application.
Alright, so let's start with the basics of documents on iOS.
So this is a sample of what a document might look like by default, and the frameworks provide a view for you that represent a document.
This is left up to you to build in a way that's appropriate for your look and feel in your application.
To the operating system, know you specify documents by URL and these will be file URLs.
Here it's going to be the file type and there'll be some path into the sandbox in your application and in this case, the last path component is Beach Party.pdf.
And we'll extract that last path component as the name that's visible to the user, and then we'll also extract the path extension from that name, and from that we're going to derive a UTI.
We use UTIs for a wide variety of purposes.
In this case, for PDF, the UTI for that is com.adobe.pdf.
Now, maybe then in your application, the last path component of URL isn't appropriate to display to the user.
For example, if you use some sort of unique identifier for a document, in this case, you can actually replace the name, in this case, Beach Party.pdf and we'll still derive the UTI from the extension of that name.
You can also specify the UTI separately if that can't be derived from the extension of the name.
And finally, documents have icons associated with them.
These icons are provided by the application that registers itself as the owner of this particular file type.
And so for the Pages document for example, the Pages icon will come from the Pages applications.
OK, let's move on to registering for documents.
Just like on the desktop, if you're familiar with Mac OS X development, you register for documents via your applications Info.plist.
And there's a specific key in there called CFBundleDocumentTypes, and this is the exact same key we use on the desktop to register for documents types.
And this is an array of dictionaries.
Now, these dictionaries can have quite a number of keys.
The documentation goes over all of the possible keys, but there are four that we're going to sort of touch on briefly today.
The first is CFBundleTypeExtensions, and this is an array of extensions that you know how to open, in this case, PDF.
The second is LSItemContentTypes, and this is just an array of the UTIs that are associated with these extensions, and so in this example com.adobe.pdf.
The third is CFBundleTypeName, and this is just the user visible string for this type of document, not for the specific document but for the generic type, in this case, PDF document.
And finally, LSHandlerRank, and this allows you to fine tune the document's association with your application.
There should be one application on the system which is defined as the owner typically.
In the pages example we talked about, that would be the page's application.
It is the only one that actually owns pages type documents.
But if other apps on the system like yours can open pages of documents, you would specify yourself as default or alternate and the documentation goes into some specifics as to why you might choose one over the other.
If you just leave that key out, you'll get the default behavior and that's typically the most useful.
So this is what it looks like in your Info.plist.
You have the key CFBundleDocumentTypes and as you see, it's an array of dictionaries, so this whole bundle worth of key value pairs could get duplicated for other extensions that you might want to register for.
First key CFBundleTypeExtensions, as I mention, that's an array of strings, in this case PDF.
LSItemContentTypes is also an array of strings, in this case, com.abode.pdf.
And CFBundleTypeName is just a simple string in that PDF document.
OK. So, now you've registered your application to open a certain document type.
How do you actually open that?
Well, you do that with a couple of methods on the UIApplicationDelegate Protocol.
So your application's delegate will receive these methods when needed to open a document.
The first is application didFinishLaunchingWithOptions.
So right after your application is launched, this is one of the first methods you'll receive.
And that Options dictionary as the second argument there has a large number of parameters, but there are three that are specific to opening documents.
The first is UIApplicationLaunchOptionsURLKey and this is just that URL for that document that you're asked to open.
The second is UIApplicationLaunchOptions SourceApplicationKey, and this is the bundle identifier of the application which has asked that this document be opened.
So if users in mail and they click on a document to open it and your application launches, you'll be sent the bundle identifier of our mail application.
And the final key is UIApplicationLaunchOptions AnnotationKey.
Now, this is just an extra bag of information which is of a plist type that you can use if you want to pass information from one application to another.
So let's say you have a suite of applications and you want to transfer a document from one to another and pass on a little bit of extra information so that resulting application can, let's say, open to the same spot in a document or something like that.
This is a good way to do that by default from nearby applications, this key won't be present.
So, when you're sent this message, you're not expected to actually open the document inside this method.
This is just an indictor of whether you can open it or should open it.
The return value for this is a Boolean.
And if you return yes from that, you'll be sent a second method which is application handleOpenURL, and this is where you actually need to open the document.
In fact, if your application is already running, you'll only receive the second method there to open a second document.
With that, I'd like to bring Luke up on stage and he can run to a demo of what we covered at this point.
Luke Hiesterman: OK, thank you, Jason.
So what I want to show you guys today is a practical example of how do we actually register to open some documents and then how do we actually go about opening those documents.
So, as Jason talked about before, registering for documents is as simple as putting some keys into your Info.plist.
So, I'm going to show you what that actually looks like in Xcode.
You got to see the XML before, so let's zoom in on this.
Hopefully, I'd see a little better.
OK. So this document is called PDF viewer, and it registers for two types of applications or two types of documents that is, text and PDF.
So here we have CFBundleDocumentTypes which as Jason said is an array of dictionaries.
The first dictionary here is going to register for a text document, and so we have CFBundleTypeExtensions, so it's text.
And we have CFBundleTypeName, that's the text document.
And I'm also going to provide an icon file which uses CFBundleTypeIconFile.
And we set the UTI using LSItemContentTypes which for this public all text.
And then we do the same thing for PDF documents.
I have CFBundleTypeExtension PDF, CFBundleTypeName PDF document.
Again, I defined an icon file which is using CFBundleType icon file, and the UTI for this is comwww.pdf.
And really that's all I need to do to register to open PDF and text documents.
It's simple entries in the Info.plist and I'm done.
So, let's see what that actually does for me in the context of other applications that might use UIDocumentInteractionController to open documents.
I've installed PDF viewer along with another application called plain text viewer into my iPhone simulator, and plain text viewer is the same as PDF viewer except it only registers for the text file types.
And I have also put in this application called doc interaction which is a very simple implementation that is going to use UIDocumentInteractionController gto show us what happens when we actually register for these types.
OK. So this application is a representation of a few documents that holds the text document image, PDF and HTML document.
And simply by having registered for those document types, namely PDF and text in those two applications, PDF viewer and text viewer, we'll see what happens when in this application I press and hold on this image, DocumentInteractionController gives me an option to open in PDF viewer and that comes simply from the Info.plist keys that I put in the Info.plist.
It knows the PDF viewer knows how to handle that document type.
And similarly, if we over the text document and do the same, I get Open In PDF viewer.
This also, this Open In button that we get here, if we click on, we see both PDF viewer and plain text viewer are registered for this document type.
So, all that sort of external visibility that other applications get the opportunity to show the user that our application opens these documents come simply from these Info.plist keys.
OK, so we've learned how to register an application for document.
But probably what we're more interested in doing is opening the document, when we actually are asked to by another application.
So, as Jason talked about, we need to use some methods on the application delegate.
So this application delegate already has application, did Finish Launching With Options, and now it returns Yes.
This code is going to be very familiar to you.
But what is going to be interesting for us to open a document is to use application: handleOpenURL.
Now this application, PDF viewer, is going to have a very simple implementation.
It's just going to use a web view and load any URL that comes into it.
We're going to take advantage of the fact that web views know how to handle text content and they know how to handle PDF content.
So any text or PDF document that comes through us, we can just load into our Web View.
So, let's implement application handleOpenURL.
The first time we run it, we just need to create our web view and add it into the window.
That's very simple.
The actual work is going to be done simply by loading the URL that is sent to us as the argument to handleOpenURL.
So that's simply WebView loadrequest using an NSURLRequest with the URL that's handed to us.
And application: handleOpenURL takes a Boolean argument, so we're just going to return "yes" that we successfully open the document, and that's really all the code we need to write to handle opening a document in this case.
Obviously, your application is likely to have unique things that you do to a document.
But the important thing to remember is that application handleOpenURL is the place that you need to implement your special code that does what you do best to use the documents.
So, let's go back to our simulator and just see this working.
We'll see that we actually can.
Now, open a text document in PDF viewer, and there it is.
Very simple text document but our viewer is able to open it by running the code in application handleOpenURL.
So, that's how simple it is to register for an application or to register for a document type and then to open it.
Remember, we register using the Info.plist keys, and then we open the document by implementing handleOpenURL, which can be called any point in the application lifecycle at launch or later.
Perhaps if your application is backgrounded, they might be brought back to the foreground to open a URL.
So that's registering for and opening documents using UIDocumentInteractionController.
So, I'll hand you back to Jason.
Thank you, Jason.
[ Applause ]
Jason Beaver: Thanks, Luke.
OK, let's move on to interacting with documents and specifically using the new UIDocumentInteractionController class to provide a lot of the same functionality that we just saw with demo.
So, as we mentioned, we don't provide a of a view by default to attach or to represent the document and to attach these to do a to a DocumentInteractionController, but this is what it might look like.
This is mail with a couple of different documents.
And what does the DocumentInteractionController actually do for us?
Well, the first thing it can do is actually present a full screen preview of these documents.
This is what it looks like on iPhone and iPad.
The DocumentInteractionController can also present an Options menu.
This gives the user a few high level choices, whether they want to Quick Look it, which is that preview functionality we just saw, or open it in whatever the default application for that document type is.
Or if there are multiple applications, you can even open the choose Open In and pick one of the other ones.
If they do pick Open In, the DocumentInteractionController can automatically take care of presenting the user interface to show all of the applications which register for that document type on the system, and it takes care of the differences between iPhone or iPad just like we're seeing here.
So, the first thing we need to do is create a UIDocumentInteractionController, and we do that with the class method interactionControllerWithURL.
Next, there are a few different properties we can use to configure the DocumentInteractionController, the first is that the URL, so even after you create it, you can change the document that the DocumentInteractionController refers to by just replacing the URL.
And like I said before, you can override the name in UTI if they can't be derived from the URL that you give us.
So, that's almost all the information that you need to draw your view.
There's one more piece that you need, and that is the icons.
So there's an additional property on UIDocumentInteractionController called icons, and that just returns an array of UIImages that the owning application has defined for this document type.
And these UIImages are sorted from smallest to largest so you can look through there and find the one that makes the most sense for you in whichever, whatever way you're presenting it.
So with that, we have all the information that you need to define your view and present it to the user.
But we still need to tie the view that you've provided to the DocumentInteractionController that you created on the backend so that you can get some of this automatic behavior.
Fortunately, it is really easy to do that.
There's an additional property on the DocumentInteractionController called rs.
Now, Gesture Recognizers were introduced in iOS 3.2.
And if you haven't had a chance to take a look at them, I strongly encourage you to.
Gesture Recognizers allow you to decouple gesture recognition from your view's implementation.
And so, all you need to do to get the default set of Gesture Recognizers that we provide so that your views feel like every other view on the system that represents documents is to assign your view's Gesture Recognizer property to the Gesture Recognizer property returned by the DocumentInteractionController.
So what does that actually get you?
So again, we're here is our example with a couple of document views.
And what I get you right off the box is if a user taps on your view, full screen preview will get presented.
If instead of tapping on your view, they press and hold on your view for a moment, they'll automatically get the Options menu.
And if they click on the Open In button there, they'll automatically get the Open In menu.
So there's one more bit of work that we need to do to actually support the Quick Look behavior so that you can get that full screen preview.
You need to assign the delegate of the DocumentInteractionController to some object in your application, and then there's a few methods that you need to implement.
The first one to support Quick Look as required and that is documentInteractionController ViewControllerForPreview.
Basically, what happens under the cover is that the Quick Look framework provides a view controller to display that preview.
But it needs to get presented somewhere in your application, and it's going to be presented modally on top of a view controller that you provide.
This is the way you return that view controller so that we know where to present that full screen preview.
Now on iPad, there are a couple of more methods that you can implement to control that scale animation that comes up.
If you've actually played with attachments in mail on iPad, you'll know that the full screen preview zooms up from that document icon, and you can do that by the methods documentInteractionController ViewForPreview and documentInteractionControllerRectForPreview, and we'll talk about those here in a second.
So, in this sample, this is mail, the view controller's view is that entire message view, so the entire size of the screen.
But we don't want the preview to scale up from the full screen so we need to implement that method documentInteractionController ViewForPreview to return the specific view that we want to scale up from, in this case, the one that the user clicked on.
But we really don't even want to scale up from that full view, we really want to scale up from that icon inside the view, and maybe there isn't a view associated with that.
If the view that you've provided simply uses a drawRect to draw all the pieces, that's where that last method documentInteractionController RectForPreview, that lets you specify the subRect within that view where the preview should start scaling from.
Alright, there's a bunch of delegate notifications that the DocumentInteractionController sends to you that you can use to change your UI in response to various actions happening automatically by the DocumentInteractionController.
The first pair we're going to look at is documentInteractionControllerWillBeginPreview and DidEndPreview.
So if the user taps on your view and that preview appears, you'll get these notifications when the preview ends or begins.
And then when the user clicks the Close or Done button, you'll get the second notification that the preview ended.
There are similar pairs of methods to be notified when the Options menu first appears or is dismissed, and there's a similar set for the Open In...
menu. So when that pops up, you'll get that first notification.
When the user dismisses it either by clicking the Cancel button on the iPhone case or just anywhere outside the popover in the iPad case, you'll get the DidDismiss.
Now, if the user actually clicks on one of those applications to open it in another application, your application will receive the documentInteractionController willBeginSendingToApplication.
And once the copy has made it over to the other application sandbox, you're sent documentInteractionController didEndSendingToApplication.
And this is a really good time for you to clean up any temporary state that you had that was associated with that document.
So again, the user clicks on one of these, you'll receive these methods in sequence as that icon or as the document is copied over the other applications sandbox.
Now, you can also control that Options menu, and what appears in there with this method or this pair of methods here, documentInteractionController canPerformAction.
Now, right now in UIDocumentInteractionController, there is one additional action that you can associate with that Options menu, and that's the Copy action.
You'll be sent documentInteractionController canPerformAction and the actual will be the copy action.
And if you return Yes from that, will automatically insert a Copy menu item in that Options menu.
And if the user clicks on that Copy menu item, you'll be sent the second method, documentInteractionController performAction.
And although right now, the only methods we send or the only actions we send are the copy actions, you shouldn't assume that will always be the case.
We may add other actions overtime, so be sure and sort of code defensively make sure you're checking for the action that you want.
With that, let's bring Luke up again and take you to all of these.
Luke Hiesterman: OK, thank you again, Jason.
Alright, so in the last demo, we saw how to register for the document type and then how to implement handleOpenURL in your application delegate to actually open a document.
Now, to build on what Jason just talked about, I want to show you how to actually create a UIDocumentInteractionController in your application so that you can provide the user things like preview and ability to send a document to other applications.
And then also, we're going to show you a few examples of using the properties and the delegate methods of UIDocumentInteractionController to customize it a little bit.
So, let's start by going into these applications, this doc interaction, and I'll show you I'll begin just by showing you what it looks like.
OK, so this is similar to what you saw before, sort of an unfinished version and it represents the four documents, text, image, PDF, and HTML.
But it really doesn't do much else other than display some text and, you know, tell us what the document is.
There is no icon there.
I can't do anything by clicking on it.
It really just is text.
So, we want to enhance that by using our document interaction controller to give the users some interactivity with the documents, and also to enhance the look a little bit.
So, let's go into my custom UITableViewCell code, and it has this method, set document name, the extension, index, and table view controller.
And it's simply a method on the table view cell to associate a document with the table view cell, and we're going to compose that with the document and its extension and the index in the table view controller give us some back preferences that we'll be able to support interaction with the controller in preview.
So, in order to set up UIDocumentInteractionController, we're going to need a URL.
So we're going to build that URL from the file path which we've gotten from NSBundle using a document and extension.
So we get doc URL simply by alloc initing with the file URL path.
No one used that URL to actually build UIDocumentInteractionController.
So let's create one with alloc init and then we set the DocumentInteractionController's URL property to the URL that we built.
We also need to set the delegate of the UIDocumentInteractionController.
We'll set that to self and then we can go ahead and release the document URL.
So, now we've created a document interaction controller.
We want to use it to enhance the look of our cell a little bit, so we're going to take advantage of the icons property on the document interaction controller to actually be able to put an icon in the cell that represents the document.
The icons property is an array of icons ordered from smallest to largest and we're going to get the count of the icons from the DocumentInteractionController.
As long as that's greater than zero, we will just take the last one, which will be the largest, and set that as the image of the cells image view.
So using just that, we can run our application.
And now, simply by creating a UIDocumentInteractionController and using the icon from it, you see that we've improved the look of our application a little bit.
We now have icons associated with the various document types.
The thing that we don't have is interaction.
So, DocumentInteractionController, as Jason talked about before, comes with a great way to add canned, consistent interaction that the user will recognize across all applications in the system, and we do that via the gesture Recognizer's property.
The gestureRecognizer property, as Jason said before, gives us two gesture Recognizers.
One that if you user taps on the view, it will provide a preview, and two, if the user presses and hold, that's UILongPressGestureRecognizer.
It will present the Options menu from the DocumentInteractionController.
So actually, adding the gestureRecognizer to your view is as simple as this line, self.gestureRecognizers = documentInteractionController.
Now, if I had other Gesture Recognizers on my view, I could have gone through a loop and added the Gesture Recognizers from the DocumentInteractionController to my view one at a time.
But in this case, I don't have any other Gesture Recognizers so it's very simple, I can just do self.gestureRecognizers equals the ones from the DocumentInteractionController.
So, now that I've done that, I need to do one more thing to actually support Quick Look and preview in my application.
And that's to implement the delegate method documentInteractionControllerViewControllerForPreview as Jason said before.
Now, I've saved a way, the table view controller in my setDocumentName method.
So for me, this is as simple as returning that table view controller.
So now we have documentInteractionControllerViewControllerForPreview, that simply return the table view controller.
Now that DocumentInteractionController has a view controller to provide the preview on, we should have all we need to support both preview and opening the Options menu on our cell.
So let's take a look at that.
OK, everything looks the same as before.
Now, let's see what happens when I tap on a cell: Preview pops up; that's exactly what we wanted.
We got that simply from setting the Gesture Recognizers on our view, which is the cell.
Furthermore, if I tap and hold on say text document, I get the ability to I get the Options menu which gives the user a Quick Look option and also the ability to open in any other documents that are registered for that document type.
Both of those actions came simply from setting the gesture recognizers on the view.
And of course implementing ViewControllerForPreview Support Preview.
OK, so that's good.
Now I'd like to customize a little bit.
If you look at this view, we see we have the document name is Text Document, dub text actually.
And that has survived form the URL that we provided to the document interaction controller.
Now Jason said before, we divide the name from the URL unless you derive the UTI from the name.
So I want to give it a custom name, say I just want to call it "my doc" or whatever makes sense for my application.
I can do that by going back to my code where I create the document interaction controller.
And I'll simply change the name, DocumentInteractionController.name = mydoc.
Let's give that a shot.
OK, so now I QuickLook my doc, or rather text document, when actually it's called "my doc".
But something weird happened, our icons went away.
Why did that happen?
Well, as I said and as Jason said, the UTI is derived form the name and I change the name to an obviously ambiguous one "my doc," how could we possibly know what kind of document that is?
So if I'm going to override the name to something ambiguous like my doc then I need to also override the UTI so that the document interaction controller knows what kind of document I'm talking about.
Now in this case, all I ever really wanted to do was change the name so rather than trying to calculate the UTI myself from the URL, I'm going to let the document interaction controller do that.
So the point that I change the name, the DocumentInteractionController already knew the UTI.
So I'm just going to store that away before I change the name.
Create a variable called UTI and store the DocumentInteractionController.UTI which is an NSString.
Then after I set the name, I simply restore that value by doing DocumentInteractionController.UTI = UTI.
I'd set the UTI right back after I change the name.
So now doing that, I can run again.
I got my icons back and I still have my custom name "my doc."
So that's how I can use the name property and the UTI property to customize the name that you'll see in the preview a little bit.
OK, so now, let's look at this one more time.
You see I have these checkmarks on the UITableView cells.
Right now I'm not doing anything.
But my intention is that this checkmarks indicate a document that is not read.
And I want to respond to the user actually sort of opening or looking at the document by removing that checkmark and indicating that this is a read document.
I can do that by using a couple of delegate methods from the document interaction controller.
Namely, I'm going to use documentInteractionControllerDidEndPreview.
And it's also possible that the user instead of previewing looked at the document by sending into another application.
So I'm also going to implement documentInteractionControllerDidEndSendingToApplication.
So this code is going to be very simple.
We'll start by implementing documentInteractionControllerDidEndPreview.
So my cell knows how to display itself by the document read property.
Namely, if the document is read, don't show the checkmark and if it is not read do show the check mark.
So all I need to do in my didEndPreview method is change the value of document read so do that setting to "yes".
And the second things I'm going to do is simply notify my table view control of the read state.
And that's all I need to do.
My cell will update its view based on the document read state and I'll be done.
Now let's go ahead and see that running.
You see now that I can preview the image.
And when I let it go, a check mark goes away indicating to the user that I've read the document, or in this case, viewed it.
So the other case is if the user actually opens the document in another application.
For that, we're going to use documentInteractionControllerDidEndSendingToApplication.
The code is going to be exactly the same because I'll again all I need to do is update the document read state of my table view cell.
So, we set document read again.
And again, notify the table view controller.
And with this very simple code implemented in Interaction Controller didEndSendingToApplication, I'll be able to use the same document as read feature when I sent the document to another application.
So let's try sending this document to the PDF Viewer which we looked for.
OK. PDF viewer displays the text.
And now, since this is iOS 40, our application is suspended in the background and when we go back to that application it will continue running and we've gotten our documentInteractionController: didEndSendingToApplication, so the viewer when they return to the application see that our text document is now read.
So using those simple delegate callbacks we're able to add a little bit more polish and custom behavior to our application.
And in that, we've seen just how simple it is to add a UIDocumentInteractionController to your applications so that you can begin to allow the user to Quick Look documents as well as send them to other applications and we've also seen how we can customize that a little bit using the name and the UTI properties and using the delegate callbacks to add more polish to our application.
So let's getting started building a UIDocumentInterActionController and I'll hand you back to Jason again for some more sights.
[ Applause ]
Jason Beaver: Thanks again, Luke.
So that shows you how easy it is to use the built-in Gesture Recognizers to provide a bunch of automatic interaction with your view.
But it may not be appropriate in your application for tap to bring up the preview or press and hold to bring up those options menus.
Say for example you've that the view that represents a document in your application is a cell in a table view and you want tap to be able to select that row.
You don't have to look up those gesture recognizers.
All that same functionality is exposed in methods on document interaction controller and so you can drive all those interface elements directly.
Start with Present Preview.
There are two methods that you want to know about.
The first is presentPreviewAnimated and dismissPreviewAnimated.
When you call that, a full screen preview will be shown just like it would if the user had tapped on it and you have the Gesture Recognizers hooked up and we'll use all the same machinery under the covers, the view controller that you return to us direct the view, all those sort of things are used to present the preview.
You can also directly present the options menu.
There are two ways you can do this.
The first is presentOptionsMenuFromRect inView.
That will give you that for scale up that we talked about.
The other is presentOptionsMenuFromBarButtonItem.
And if you want to dismiss the Options Menu you do that with dismissMenuAnimated and you'll get the option menu.
Similarly, there are cool set of method to present the Open In Menu, presentOpenInMenuFromRect: inView: animated: and presentOpenInMenuFromBarButtonItem: animated: and use the exact same dismissMenuAnimated method to tear down the Open In Menu.
If you do that again, that forces the Open In Menu on screen.
So let's now look at a demo where we use an alternate way to present some of this UI from the Document Interaction Controller.
Luke Hiesterman: So in the last application, we saw basic usage of creating a UIDocumentInteractionController, when using the Can Gesture Recognizers to add some interactivity to our view.
But as Jason just walked through, you know, sometimes we want to explicitly show the preview in code via code or show the options menu via code or even the Open In Menu.
So I'm going to go back to your code here and we're going to implement basically the same thing we had before but we're just going to modify to show you an example of how we might do this if we wanted to handle the showing of the preview and the options menu ourselves.
So the first thing I did, there you see as I commented out self to adjust to recognizers equals documentInteractionContoller.gestureRecognizers.
That was the thing that did all that work for us before.
We want to make life a little more difficult this time.
And so what we're going to do is we're going to allow the table views, didSelectRowAtIndexPath method, that's going to be what we're going to use to display the preview and the user selects a row.
We're going to display the preview.
And then we're going to move our sort of long press gesture to the actual image view in the table view cell.
So let's start with that long press gesture.
If you're not familiar with gesture recognizers they, you know, go on a specific view and they calculate the gestures that happened on that view that did the touch handling for you and hopefully you'll go to the gesture recognizer session tomorrow.
So this is going to be as simple as creating a UI long press gesture recognizer, just like that, we alloc init.
Gesture Recognizers use a target action pattern.
So we use self as a target and the action is "handle long press," which is a method that we'll write in a second and then we just need to add that Gesture Recognizer to the image view.
Since we're going to use a long press now, only on the image view not the entire cell itself.
So, we have image view adjust Recognizer.
Now, we can release our gesture.
And maybe one more thing because image views have user interaction disabled by default, we're going to enable user interaction on our image view.
So we used the action "handle long press" for our Gesture Recognizer.
So, it's something that we need to implement and this is why we're going to actually tell the document interaction controller to present the Options menu.
OK. So handle long press it's going to look sort of like this in the shell.
Gestures Recognizers use a series of states from possible begun, ended, cancel, change, things like that in their action.
So all we're interested for a long press the begun state, because that's when the user has pressed and held long enough for a long press to begin.
So in that begun state, now all we need to do is tell our document interaction controller to present the Options menu.
That looks like this, documentInteractionController: presentOptionsMenuFromRect: inView: animated.
Now, as Jason touched on before, the rect and the view don't mean anything on an iPhone because we just display an action sheet.
On iPad this would be the view interacts that the animation for the preview comes from.
So that's the long press.
Now, we're going to go to the table viewController.
Now we need to do is implement table view didSelectRowAtIndexPath and tell the DocumentInteractionController to present a preview since we want to do this manually now.
So in didSelectRowAtIndexPath, first thing we need to do is actually get the Document Interaction Controller.
Each one of our cells has a Document Interaction Controller associated with it.
So we need to get the cell from the table view.
Table view cell for RowAtIndexPath and then query that cell for its Document Interaction Controller, that's what we do here.
So now we have the Document Interaction Controller, we just need to tell it to present its preview.
DocumentInteractionController: presentPreviewAnimated: yes.
So we've eliminated the self to adjust to recognizers equals documentInteractionController.gestureRecognizers and we've used actual explicit calls to the display the Options Menu and the preview.
And we'll see that work now.
When I select the cell, the preview is displayed for us just as before.
And similarly, when I go and if I long press on the cell, we'll see it doesn't do anything anymore since we're no longer using the gesture recognizers on the cell itself.
But if I go over here and I long press on the image view which is the icon I get my Options Menu again which I am manually displaying with the code.
So obviously, it's very easy and in general we're going to recommend that you use the canned gesture recognizers that come with UIDocumentInteractionController.
But if it makes sense for your application to manually present the Options Menu, the opening menu or the preview in code that's exactly how you do that and you see of course it's very easy.
So that's all I need to show you there and I'll hand you back to Jason once again for some more sights.
[ Applause ]
Jason Beaver: Thanks again, Luke.
Alright, finally, let's talk a little bit about the Quick Look Framework.
As I mentioned before the Quick Look framework sits below the Document Interaction Controller and provides that full screen preview but you can control that more directly if that makes sense in your application.
So what does it provide to you?
Well it provides a dedicated view controller that's used to preview documents.
Here's a simple example of the preview print and preview in a numbers document but the QLPreviewController supports a wide variety of document types.
We support PDF, HTML, and a wide variety of rich formats rich text formats as well as a wide variety of plain text formats, a large number of image types are supported, contacts, and events can be previewed directly as well as all of the Apple iWork document types and Microsoft Office document types.
And if you were, like I said before, familiar with Quick Look on the desktop, the API is very similar on the device.
So to get started, all we need to do is create a QLPreviewController and we do that by importing the Quick Look header out of the Quick Look framework and then simply allocating and initializing the QLPreviewController.
Now this is just a subclass of UIViewController so you present it like you would in any other view controller.
You can either use UIViewControllers presentModalViewController: animated: method which will present the preview controller in a modal fashion on top of your view controller and that's what the Document Interaction Controller typically does or you can push a view controller onto a navigation stack.
Now the Document Interaction Controller actually will also do this one too if the view controller that you happen to returned to us is a navigation controller.
So that gets the view controller on screen but we haven't given it any content yet.
The QLPreviewController uses a data source model to get its content and there are two methods you need to implement to provide data to the QLPreviewController.
The first is numberOfPreviewItemsInPreviewController: and the second one is previewController: previewItemAtIndex.
So here's a simple sort of graphical example.
In your model, you have some number of items that you want to preview.
And using these two methods, you tell the preview controller how many you have and when asked by the preview controller you return the appropriate one at that index.
Now in order to actually display these, these items need to implement the QLPreviewItem protocol.
So what's that?
Well first I should mention that there is one class in our system that already implements the QLPreviewItem protocol and that's NSURL.
So if the URL for your document expresses everything you want to display the last path component has the name that you would like to display visually, you can just return the URL to the QLPreview Controller.
Your document will be previewed.
But if you want to change that name that appears at the top like we did in the Document Interaction Controller, you'll want to provide some alternate object and this can be a really simple little object.
There are only two methods that you need to implement in the QLPreviewItem protocol.
The first is that URL.
You obviously already had that so just return it.
And the second is the title which is the thing you want to display at the top.
So again, this is a really tiny little class.
It's trivial to implement if you do need to change the behavior.
There are a few delegate methods that are interesting that the QLPreviewController offers.
The first two are notifications on dismissal of the preview.
When the dismissal is about to happen and after it did dismiss, you're sent these delegate messages.
The third preview controller should open URL for preview item is called if the document that's being previewed has a clickable link in it and the user clicks on it, you're sent this message and there's a Boolean return here and you can indicate whether the user is allowed to open that link.
With that, let's bring Luke up one last time and take a look at how to use QLPreviewController directly.
Luke Hiesterman: OK, thanks Jason.
Alright. So up to this point, we've seen how to use a UIDocumentInteractionController and the various things that we can do with that.
And that's great for interacting with one document in time but sometimes we want to go down and actually use the QLPreviewController and one of the main advantages that we get from that is we can allow the user to Quick Look a set of documents sort of as a set rather than one document at a time as we saw before.
So let's go back to our application and we're going to change the implementation to use QLPreviewController.
So we're here back in our table view controller and this is the table view didSelectRowAtIndexPath that we just implemented to present the preview.
Well, we're going to go ahead and get rid of that because we're going to now do Quick Look via QLPreviewController.
And let's see just how easy that is.
So obviously, first thing we need to do is create a QLPreviewController.
Do that with alloc init.
We want to delegate in the data source to be our self.
So we'll do that.
We need to tell the preview controller what index to start at in the set of documents that we're getting that and we get that easily from the index path that was sent in to table viewed didSelectRowAtIndexPath.
So we set the current preview item index to the to the section because each document gets its own section in our application.
And then we simply need to display the preview controller.
So we do that with presentModalViewController, animate the preview controller.
Then we can go ahead and release the preview controller.
So we need to implement to the data source methods, the two data source methods, so that our preview controller will work.
We need to tell it how many preview items are in the QLPreviewController.
So we do that with numberOfPreviewItemsInPreviewController.
For us, it's very simple.
We return a macro num docs which represents the four documents that our application uses.
So that's just four, very simple.
And then we need to return a QLPreview item for each item that the user might Quick Look.
As Jason talked about before, NSURL already implements the QLPreview item protocol.
So it is a fully qualified preview item.
And we'll take advantage of that in our app to just return NSURLs for each preview item.
So previewController: previewItemAtIndex:, we're going to get the file path from NSBundle the same way we did in the cell before when we used that file patch to create a URL, same as we did before.
Set the URL to null and then we have a valid file path.
We can just create it using NSURL URLWithPath and then all we need to do is return that URL.
OK. So we've implemented the two data source methods, numberOfPreviewItemsInPreviewController: and PreviewController preview Item At Index And that would be enough to get started using the preview QLPreviewController in our application.
So now, when the user taps on the document, we get a preview, a Quick Look, just the same as before except there's one little difference and you'll see down here, there's a little bar and that actually allows the user to interact with the documents as a set.
We have a set of four that the user can now arrow through and Quick Look each one of them in turn.
Now, something else has changed.
You'll see that our check marks no longer disappear when we view the documents.
If we're going to be using QLPreviewController, this is something that we need to re-implement for the QLPreviewController since that was set up UIDocumentInteractionController before.
Fortunately for us, this is going to be a trivial task as we simply need to mark each document as read as we hit it in previewController: previewItemAtIndex.
So as soon as we create the URL, we can simply use the index that's given to us as an argument and set the document read based on that index and now when we run it, see, I can quickly click text document.
I can arrow over to image document and when that's done, both of those document types are marked as read.
So I was able to re-implement that using the QLPreview Controller.
As Jason talked about before, I could also re-implement the custom naming if I wanted to create my own QLPreview item compliant object, but in this case, I'll leave it at that and I encourage any of you who are interested to explore writing that yourself.
So that's another option that we can use, QLPreviewController.
We have both UIDocumentInteractionController and QLPreviewController.
Document Interaction Controller allows us to interact as one document at a time and the QLPreviewController allows us to use allows us to present them to the user as a set, so these are couple of options that we have at our behesty as programmers and I hope you enjoy using them.
So thank you very much and I'll hand you back once more to Jason.
[ Applause ]
Jason Beaver: Alright, thanks again Luke.
If you wanted more information, the application frameworks evangelist is Bill Dudney and his contact info is here.
There's also Documentation for the UIDocumentInteractionController up on developer.apple.com.
So the URL is a little long here but you can search for that.
And of course the Developer Forums are a great place to get your questions answered.
There's a bunch of the engineers who hang out there as well as a lot of you guys and you don't have to go through this alone.
There're a lot of people out there that can help.
There are a couple of related sessions.
We talked about Gesture Recognizers and their use briefly.
But Gesture Recognizers are really powerful and allow you to add a lot of really rich interaction to your applications.
There are two sessions tomorrow.
The first one talks about how to use all the built-in Gesture Recognizers.
The second one talks about how to write your own Gesture Recognizers.
So quickly in summary, adding support for documents in your application is really simple.
Hopefully, I've shown it's not much work for you to do that.
And so, I encourage you to do that.
And secondly, if you're going to allow interaction with documents, please use UIDocumentInteractionController.
It'll not only automate a lot of work for you, it'll provide a level of consistency in your application with all of the built-in application and other applications provided by third party developers.