WWDC2010 Session 106

Transcript

>> 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.
gestureRecognizers.
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.