Video hosted by Apple at devstreaming-cdn.apple.com

Configure player

Close

WWDC Index does not host video files

If you have access to video files, you can configure a URL pattern to be used in a video player.

URL pattern

preview

Use any of these variables in your URL pattern, the pattern is stored in your browsers' local storage.

$id
ID of session: wwdc2012-231
$eventId
ID of event: wwdc2012
$eventContentId
ID of session without event part: 231
$eventShortId
Shortened ID of event: wwdc12
$year
Year of session: 2012
$extension
Extension of original filename: mov
$filenameAlmostEvery
Filename from "(Almost) Every..." gist: [2012] [Session 231] What's New ...

WWDC12 • Session 231

What's New with Gestures

Essentials • OS X • 52:35

See what's new with gestures in AppKit. This includes new gestures for Quick Look and smart magnification, as well as new facilities for zooming and swiping. In addition, learn best practices to future proof your gesture implementation as AppKit continues to evolve.

Speaker: Raleigh Ledet

Unlisted on Apple Developer site

Downloads from Apple

HD Video (403.4 MB)

Transcript

This transcript was generated using Whisper, it has known transcription errors. We are working on an improved version.

Welcome to session 231. I'm your host, Raleiigh Ledet. Today we're going to talk about what's new with gestures. And quite a bit, actually. As you can see, we have a lot to go through. We've got some scrolling improvements that we're going to talk about, some great things going on there. We have the smart magnification gesture, which was actually released in line.

We now have public API for you to be able to take advantage of that. In a scroll view has done some gesture adoption itself, so you can take advantage of that. We'll cover that. We have a brand new NS page controller. This controller makes swiping easy, and we'll cover that.

We have the Quick Look gesture, which has been around for a while. We have public API for that, so you can catch a Quick Look gesture API and take advantage of that. And we'll wrap everything up with some best practices and how you can future proof your application with regards to gestures as we continue to evolve gestures on the desktop. So let's get right into scrolling improvements.

One of the things you'll notice right away with scrollers now is when you roll over a visible track, they automatically expand. And so this gives you a little bit wider thumb and a little bit wider track, and it's easier to grab. It's been working out really great. But one of the interesting things about this is that the track metrics are a little bit wider than they were in Lion.

In Lion, we made sure the overlay track metrics were actually the same metrics as the legacy track. And the legacy metrics haven't changed, but the overlay scroller metrics have changed. And if you depend on that, you need to be sure that you're calling this API that was introduced in Lion, scroller width for control size, scroller style, and set the right style. And you'll get the appropriate metrics. And that way you can adjust appropriately, and the metrics can change in the future, and you will just automatically adapt for that, and life will be grand.

I'm sure you've noticed right away the other interesting thing is that scrollers are not just a matter of how many scrollers you have. Overlay scrollers have a new look in Mountain Lion. We no longer have the rounded caps in the track. They're squared off. The thumbs have a little bit different look, and they actually overlap in the corners now.

And this means we also have a new look for the other knob styles. This is the dark knob style. And along with changing the knob style look, the track now looks at the knob style and adjusts accordingly as well. So this is the expanded track for the dark knob style, and this is the overlay scroller. And this is the light knob style and the expanded track for the light knob style. So you see how it works a little bit better with your content. So this is handled automatically for you just by looking at the knob style. So there's a new look.

Another really interesting thing that we have in Mountain Lion, and this only works on the Magic Trackpad or the trackpad built into the MacBooks, it doesn't work on the Magic Mouse, but when you place two fingers on a trackpad, the scrollers will automatically appear. They automatically appear over wherever the cursor is.

The view that's under the cursor, if that's a scroll view, the scrollers will automatically appear. This makes it really nice and much easier to see where you are in your content and get them to pop up when you need them. Otherwise, they're away and out of the way, and you have complete access to your content, and you can focus just on your content.

[Transcript missing]

We have added accelerated scrolling in Mountain Lion. This is an iOS style scrolling where if you continue to scroll on the trackpad or the Magic Mouse, in this case it works for both, that the scrolling will become accelerated and it will zip through your content a lot faster. The great thing is there's nothing for you to do. Scrolling delta is automatically accelerated, so this should just work in your applications now. You should give it a try and check it out.

Apple takes care of the problem of noticing when and recognizing when the user has performed three consecutive scroll gestures. And at that point, acceleration starts to kick in. And as long as the user continues swiping quickly, the acceleration will increase. If the user starts scrolling slowly once they're in accelerated mode, the system will automatically kick out of accelerated mode and go back to normal scrolling speed.

Or if the user waits too long between physically scroll gestures, between when they lifted their fingers and when they touched their fingers back on the trackpad or the Magic Mouse, will automatically get out of accelerated scrolling for you. So again, there's nothing for you to do. You should try it out in your application. It just works. In fact, let's go ahead and give you a demo of the scrolling improvements. So I have my Magic Trackpad here.

And we'll notice as soon as I put two fingers to the trackpad, the scroller appears. We've got a little thumb there. This is a very long document. It's over 10,000 lines long. It's a log file that I generated. And you can see as I normally scroll that that thumb doesn't move very much at all. But on the third consecutive scroll, you'll see the thumb starts to really accelerate as I zoom through the content. So we go one, two, three, and you see that scroller. That thumb really goes as I can get all the way through my content very quickly that way.

You notice I put two fingers down as I go into the track. It automatically expands out, a nice little animation, and it stays out until I roll back over. And if your cursor is already where the track should be when you put two fingers down, it shows up and goes away. So that is scrolling.

So let's move away from the scrolling gesture and talk about smart magnification. You might have seen this in Lion, particularly in Safari, if you did a two-finger double tap over content in Safari, that content would automatically magnify at the appropriate level. So let's take a look at that gesture. It's a two-finger double tap.

Let's take a look at it again. Two-finger double tap. It's really easy, it's really great, and it performs a smart magnification. So we have a public way for you to access this now in Mountain Lion with a new responder method, smart magnify with event. And this suggests that we have a new event type, and indeed we do. There's NS event type smart magnify.

The only properties that are really valid for a Smart Magnify event are location and window. So you can find out exactly where the Smart Magnify gesture occurred and modifiers. So you can find out what modifiers, if any, were pressed on the keyboard when the event occurred. Along with the new event type, we have a new event mask, and it's Event Mask Smart Magnify. And you can use this in your local event observers or if you're doing next event matching mask.

Though these are generally one-shot events, so it's probably not all that useful for next event matching mask, but for a local event monitor, it might be interesting. However, there's one rather large caveat with these new event type, and that they're 64-bit only. It turns out on 32-bit, we have a 32-bit mask for our events, and we've run out of bits. So we've had to restrict this to 64-bit only.

What does it mean to Smart Magnify? And to do this individually... Well, the simple case, we have some human interface guidelines for this, and the simple case is you just zoom in 150%, you magnify back out to 100%, and you toggle between the two. If the user has, through the user interface, or perhaps a two-finger pinch magnify gesture, have changed the magnification level so you're not at 100%, if you're at anything other than 100%, go back to 100%. If you're at 100%, then you can zoom, you can magnify to 150%.

But you can do better than that. In Safari, you double tap on some div content and that whole section grows to fill the screen. And so we have the more complex version of the rules from Human Interface Guidelines, which works out well depending on your content. Magnify such that the width of the target grouping fits inside the scroll view, centering the target point vertically if possible. Magnify to 100% if the mouse hasn't moved and already magnified to the deepest grouping. That's quite a lot to say, even just reading it off the slide. Let me sum it up for you. Use in a scroll view.

Speaking of NSScrollView, let's talk about ScrollView gesture adoption. As you might have guessed, it's adopted magnification and smart magnification. So you have magnification and smart magnification built right into NSScrollView for you. You no longer have to implement it yourself. And let me give you a quick demo of that.

So here we have a lovely dinner recipe in TextEdit. And just use a basic scroll view. You can see the TextEdit source code. And I'll do a two-finger double tap on this table. And you see it goes into Table. And what's interesting here is not only has ScrollView implemented Smart Magnify and Magnify, in this case, in its TextView also has a little bit more intelligence to work with ScrollView and will automatically magnify the tables, for example. And I can even go another level deep and zoom all the way into one cell.

So we can Smart Magnify again, and we come back out. So of course, we also have regular pinch zooming. So you could just use a two-finger pinch zoom, and it's nice and centered with where you're pinching and will automatically... moved the content around and it automatically rubber bands at the 1.0 scale factor for you, which is great.

TextEdit has gone a little bit further than just what TextView provides and has added some more magnification help for ScrollView. And that if I smart magnify here, it's zoomed in all the way into the content past the margins. So you could just look directly at your content and manipulate that. I'll give you another quick example.

Here's this little app. This is just a little sample app I put together. And you can zoom in there. And of course, you can also smart magnify. And it fills the content for there. Or you can smart magnify, perhaps, on the image of the dog. And it'll smart magnify to the dog. And that works out quite nicely with very little code.

So let's focus on magnification first, because smart magnification is built on top of magnification in ScrollView. There's a new property on in a ScrollView, allows magnification. There's also a checkbox in Interface Builder inside Xcode. You can just turn that on. You have to, as defaults to no, you have to set allows magnification to yes for ScrollView to start doing magnification.

We can't just turn it on by default because then lots of content and UI all over the place would start magnifying that really shouldn't perhaps magnify. But it's really easy to adopt. Along with allows magnification, there's max magnification and min magnification. By default, the maximum is set to 400%, or 4, and the min magnification is set to 25%, the actual value of 0.25. So along with your min and max, you have the actual magnification value. And all four of these magnification values are set to 0.25. So all of these properties are actually KVO compliant, so you can observe them. The most interesting one being magnification generally.

And you can just set the magnification and the ScrollView will magnify. But it happens immediately whenever you change the magnification. And that's not necessarily always what you want. If you want to animate the magnification, you should use the ScrollView's animator and then call set magnification and it will animate the change in magnification for you. And it will center that with what the content already is, which isn't necessarily always what you want.

Particularly if you're looking at a magnified gesture, you have a particular point that you want to center the magnification around, and I showed you that in the demo. And we have a nice method on In a Scroll View to help you do that, setMagnificationCenterAtPoint. So perhaps the user is doing a magnification gesture that's centered around the dog's nose here. We can call setMagnificationCenterAtPoint, and when magnification occurs, we keep the nose at the exact same spot, and that's exactly what the user expects.

We also have another method, MagnifyToFitRect. Perhaps you have a UI option for the user where they can select a portion of the image that they want to magnify, and perhaps they select this portion. You just pass the rect, and we will fit that rect as best as possible when we magnify it. And of course, In a Scroll View will respect the minimum and maximum magnification, so it will stop at the maximum magnification levels if needed in these examples as well.

Just like the magnification property, however, when you call these methods, the magnification occurs immediately. So again, if you want them animated, you just use the scroll view animator and then use the appropriate method on the animator and the magnification will be animated for you. So what's going to be really interesting to know here is exactly how in a scroll view performs its magnification. Well, let's say you have a clip view here and its frame is 0 by 90 by 90. We get a nice square. And in this case, the content fits in there perfectly. So its frame is 90 by 90 as well.

Then we want to magnify this image. Well, your document is still going to be 90 by 90. That's what it is. And the clip view frame is also going to be 90 by 90. What we do is change the clip view bounds. Normally the bounds are a one-to-one relation with the size of the view that's associated with.

But in this case, we changed the clip view bounds to 60-60 in this case. And so we're trying to expand 60 points of document data into 60 points of document data. And so we're trying to expand 60 points of document data into 90 points of view space. And this will apply the appropriate transforms to the view automatically. And you can look for this in your document by converting to the rect or size to the backing store.

And you can find out exactly how many pixels you have to work with so you can get pixel-accurate line drawing or more higher resolution drawing when you're magnified in. And you can take advantage of that. For those of you that already did the math in this example, it's magnifying out to 1.5, which is 150%. Now that we've covered magnification, we can get back into smart magnification.

And the great thing is that smart magnification handles those HI guidelines we already talked about. If you don't do anything and you've told scroll view to allow magnification, it will do the automatic simple zoom in to 150%, magnify back out to 100%, and it will handle that for you automatically.

However, Your content, you might want to do something more intelligent like Safari does, where it magnifies to the div content. But ScrollView doesn't know anything about the layout of your document view. So you need to give ScrollView a little bit of help, and you do that by implementing this method in your document view. So you don't have to override anything else.

You don't have to have a custom ScrollView class or delegate or anything like that. Just in your document view, implement rect for smart magnification at point in rect, return the appropriate rect, and ScrollView will magnify accordingly. And it gets a little bit better. Let's walk through exactly how this works.

Here we have some document view, a couple of columns of text, for example. And of course, this is sitting in a scroll view. We're not showing the whole thing at one time. And so this is the portion that was currently visible. And the user does a smart magnification gesture at this point. So we want to smart magnify the left column.

A scroll view will call REC for smart magnification at point. That would be the dot on the screen translated into the coordinate system of your document view. In REC, this is the visible REC, which is what the scroll view is currently showing. And again, translated into the coordinate system of your view. And what you return, which is really interesting, is you return the REC for this entire column. Return the REC for the entire column. And in a scroll view, we'll take a look at the whole thing.

And we'll take a look at that and look at the visible REC and we'll figure out the appropriate thing according to the current HI guidelines. And in this case, we'll magnify just like that. As you saw in the example with the table and text edit, you can continue to go further and further and deeper and deeper.

This is where you look at the visible REC to see what depth you're currently at, where the gesture occurred. And you can return a different REC for some other portion of content and keep going deeper and deeper. Once you get to the deepest level, and we only have one level in this example, the user performs.

And you can see that the user performs a smart magnify gesture. You're going to get called back with the method. And what you return that time is the exact same REC that you returned the first time, this entire column. And a scroll view will see that we're already magnified to that column, that we've already magnified to that REC as best we can.

So we'll go ahead and zoom back out to 100% for you automatically. And that's how that works. So you can let NS Scroll View handle all the logic and HI guidelines that are associated with smart magnification. And it makes life so much easier than trying to write a whole bunch of code to do it yourself.

Along with the actual magnification methods and responding to the gestures, there's a couple of new notifications, which may be interesting to some of you. In a scroll view, we'll start live magnification, magnify notification, and in a scroll view, did in live magnify notification. This occurs for both the smart magnification gesture and the pinch magnify gesture.

These are interesting because if your content can't keep up with being redrawn appropriately as the scale changes, you might want to catch the start notification and switch to a static image or switch to some lower resolution drawing model. That way you can stay responsive to the user. It's real important to always remain responsive to the user and you don't want to lag behind. And as soon as you get the did in live magnify notification, you can go back to drawing at the best resolution that you can.

So that was Magnify and Smart Magnify. Let's switch gears and talk about NSPageController. We'll talk about swiping, two-finger swiping. And NSPageController makes swiping easy. Let me show you exactly how easy that it is. Last year, I showed off this demo application called Picture Swiper. And it showed off how to use track swipe with options, dampen amount threshold. It's a big, long method.

And this method allowed you to track a two-finger scroll as a swipe. It was really interesting. And this project, as you can see, has got 300 lines of code there and another almost 300 lines of code there, another 50 lines of code. And there's some more code there and another 75 here. It's about 900 lines of code.

But it was really cool because you could do this. You have this nice little picture of the dog and you could swipe through the dog and swipe back through your images and even magnify in and you get a nice little bounce that first time and then switch images. So it's a really, really cool little app. But this is 900 lines of code. We can do better than that. And with the new NSPage controller, we do do better with that. Raleigh Ledet As hopefully you can see, we have now a lot less files here.

And this is practically nothing just to do some auto-sizing for us. So there's 24 lines of code and another 94 lines of code here in the delegate. And most of this is just pulling in the application to finish. Raleigh Ledet And I want to point out that I had taken out the big list, if you've ever seen sample code, the big, long disclaimer list that Apple has at the top.

When I counted those 900 lines of code before, that wasn't counting that. I stripped them out. Raleigh Ledet So what we really have is the page controller delegate starts at line 60 here and ends over here at line 94. So a little over 30 lines of code now to do the swiping. And if we run this.

We see that it looks pretty much the same. You can magnify in, you get that bounce, and you can go on to the next one, and you're back just like we were before. So we've gone from 900 lines of code to the new code is a little over 100 lines of code. You know, that's a tremendous savings right there for you in code, and NSPageController makes this so much easier and nicer to do.

Thank you. So, NSPageController has two modes, and we'll talk about them a little bit more, but there's a history mode and a custom mode. So, you can work NSPageController in a couple of different ways to make some different UIs. Of course, you can make a history UI, and you can do this in both custom mode and history mode, but history makes it a little bit easier, as you've seen in Safari, as you navigate through your history in Safari.

App Store has adopted NS Page Controller and is now swiping in the App Store to look through your history. You also have in Dictionary. Dictionary is implemented in NS Page Controller, and you can now swipe through your history in Dictionary online. That's really great. They've also adopted magnification on NS Scroll View, so you can magnify into the content of Dictionary, which is really kind of cool.

Reminders, the new Reminders app. This is a little bit different. You're not navigating through history here. You're navigating through a list of reminder cards, but Reminders in this case also is using NS Page Controller, and they've adopted that as well. Game Center has adopted it. As you've seen, the cards, the images slide. You have similar slide animations like this when you're changing screens, for example, or as you're swiping through application pages in Launchpad. Some different interfaces that you can apply.

The first thing to know about NS Page Controller, we're going to talk a little bit about how it's constructed, and then we'll get into each of the modes and how each of the modes works a little bit. NS Page Controller is an NS View Controller. That's what it subclasses from, so it starts off as an NS View Controller. This is something new in the kit. We normally don't have custom view controllers. We have views, but in this case, Page Controller is a controller for a view, and of course, there's going to be a view.

You need to wire this view up in your interface in Interface Builder and provide the content. But NSPageController will then be responsible for controlling things in that view. Unlike NSViewController, NSPageController automatically inserts itself into the responder chain for you, so you just wire it up to your view and you don't have to worry about the responder chain.

In addition to what NSViewController provides, NSPageController also has a concept of a transition style. We have three different transition styles. There's StackHistory, StackBook, and HorizontalStrip. I want to explain them a little bit more in detail. When you're looking at a history type of interface, like an app store or in Safari, you start off showing page one content.

Then the user is going to navigate somewhere and all of a sudden there's going to be page two content. Well, where sort of logically is page one? It's now behind page two. As the user continues to go forward and adding new content, those pages get added on top of the current pages that are already there.

So, if you want to swipe back to page one, the animation works that way. If you want to bring page two back in, if you want to move forward to page two, you bring page two on top of page one. So, that is how history works and that's the animation for history.

But perhaps you have something that's more like a book. Reminders uses this transition style, for example, because they have a list of cards. But for an example, it would be multi-page PDF. So you're seeing page one. Where is page two? Well, logically, just like a book, page two is going to be already behind page one. So we want to move page one off to the side and reveal page two. If we want to bring page one back on, we of course want to slide page one back over page two.

So the animation is a little bit different, but it really works quite naturally when you use it in practice. There's also the strip, which I'll cover briefly. And this just lays your pages out horizontally next to each other. So they slide in like that, and you can move back to page one that way.

I want to make a real important note here that these transition styles are actually independent of the mode. We've talked about those two modes, and we have three transition styles. They're completely independent from each other. I'll talk about that a little bit more, but you choose which mode you want to use, and then you need to choose the appropriate transition style for your user interface.

In this page control, we also have the concept of an array of arranged objects. These are just references to however you want to organize your pages and a selected index so we know what the current page is. And using the count of arranged objects and the selected index, it's easy to tell how many pages you can go forward and how many pages the user can go backwards.

There's three interesting IBAction methods that NSPageController implements and exposes. Navigate back, navigate forward, and take selected index from. These are really interesting because they're designed for you to wire up your button in Interface Builder right to the page controller, and now you can navigate forward and backward, and it's animated for you, and it works out really nicely. Quick aside, if you wire up take selected from index, the sender of that message needs to respond to integer value so we can get the appropriate selected index.

Programmatically, you can set the selected index by calling setSelectedIndex new index. And this happens immediately. If you want to animate it, you of course need to use the animator. But Page Controller works a little bit differently when you're animating content than you would think of a normal animator because we're bringing in a completely new content here that is not there. We're not just magnifying the content. We're bringing in something completely new.

And sometimes this takes a little bit of time. So one of the concepts that Page Controller has is when it does an animation, you must inform it when to complete the transition so that it can hide the animation that's going on and reveal your view once your view is ready to draw. And that way it's seamless to the user.

And when you call the animator setSelectedIndex in this example, you are still responsible for calling complete transition. And so you need to do that in a completion handler. And we made that real easy with an API on NSAnimationContext. Run animation group. Inside your group, you ask page controller's animator to set the selected index. And then in the completion handler, you call complete transition. Raleigh Ledet I'm going to cover complete transition more in the next section at the end of the next little section about page controller.

So if it's a little bit confusing, don't worry about it. We're going to get to it shortly. So if you're in -- once you use Page Controller in History Mode, what we were looking at when we designed History Mode is we wanted to manage the history for you, and we wanted to be responsive to what the user is doing at all times and make it as simple as possible for you to implement.

And so it works like this. So you have-- NSPageController has an NSPageController delegate. Your view that you attach to Page Controller is the content. You draw whatever content you want in this view. You don't need to worry about the arranged objects. You don't need to worry about the selected index. Generally, they don't even access those properties. Whenever the user navigates to some new page, you simply call Navigate Forward to Object on NSPageController. In this case, you set what the new object is, page two.

Something really interesting happens here when that happens is NSPageController takes a snapshot of page one and hangs on to it. And then once your code gets back from calling Navigate Forward to Object, you can show the contents of page two. Just like history, and when you're doing history in a web browser, if you navigate three or four levels deep and then you go back a few pages, then you start navigating in some new direction, those pages that you came back from that were on the forward history stack have a new direction. gone away.

Navigate Forward to Object does that as well for you. So it will add page two, the next index on the Page Controller, and it will automatically drop any forward history that needs to be dropped. And then finally, when we get your return, you can draw your content for page two and continue to respond to the user events. At some point, let's say the user now navigates and they want to swipe back to page one.

Your delegate will be called with Page Controller will start live transition. This is an optional delegate method. It's useful, however, for you to stash away state that you might want to restore. For example, you might want to store the scroller position and navigate back to it. So when you navigate back to page one, you can reset that scroller position. So there's Page Controller will start live transition.

We take a snapshot of page two immediately at that point, and then something really interesting happens is we hide your view. And Page Controller puts in its place a custom animation view hierarchy, and we bring back those snapshots in. And this way we can remain immediately responsive to what the user is doing, and it provides a real smooth and real nice interaction for them.

The user continues the swipe gesture and releases their fingers from the trackpad. And at this point, they've swiped far enough with enough speed that we've decided that this gesture has completed successfully. The animation hasn't completed, but the user has performed the gesture successfully. Your delegate is called with page controller did transition to object. Again, this is an optional delegate method, but in history mode, it's one that you're likely to implement.

You're given the page that we're transitioning to right there in this method call. So again, you don't have to look up the selected index from the arranged objects. You just handed it. It's real easy. You now just start getting set up to display page one. Your view is still hidden, so you can start setting properties on your view. If you need to start loading something, perhaps some network access, you can start doing that here as well.

The animation then continues, and the delegate is called with a page control it did in live transition. This is the delegate method that you really, really, really, really need to implement. And what you do when you get notified that the live transition has ended is this is where we need to inform Page Controller to complete the transition. Because at this point, Page Controller still has up its custom animation thing. Normally, if you draw fast enough and everything is fine, if you're not doing any network loading, you can just call complete transition right here.

Other times, you need to note that the live transition has completed. And once your network access has finished, you can call complete transition when your view is completely ready to go. That way, when we drop the snapshot that we had taken earlier, it will exactly match the view.

If you call complete transition too early, you'll have perhaps a blank view or perhaps white in there and you'll get this flash and it won't be a good user experience. So that is why complete transition is vitally important. And that's history mode in a nutshell. Real easy to use.

Again, in history mode, you will have to set up the transition style when you're setting up in as Page Controller. It's real easy to do in Interface Builder. You just set which transition style you want. And you're likely to want to use the stack history style in that case.

Custom mode is all about continuing to be responsive to the user, but giving you, the developer, more control over how NSPageController works and taking snapshots. If you're performing a book-style user interface, you'll probably need to use custom mode, and that'll be a little bit more apparent as I continue.

You can also do a history-style user interface if you need more control over exactly how it's done. That's another reason why the transitions are independent from the mode. You could set up the appropriate transition style and page controller for custom mode for the type of user interface you're building.

If you build a custom mode, the way you trigger NSPageController to use custom mode is you implement PageController identifier for object and PageController view controller for identifier. This is very similar to what View-based table views does, except that in returning views, we ask for a view controller. We will also cache the view controller for the identifier, and we will cache as many as we need and only ask you to vend view controllers as needed. We'll go through an example of exactly how that works.

Another major difference between custom mode and history mode is that your view in this case is not your content. This view is just a container for the content. Remember, you're going to be vending view controllers which have views and we're going to be using those views. What's really interesting to do in the view that you wire up to page controller is to draw a background there. Treat this view as a containing background view.

As the user is swiping, particularly at the beginning or the end of the content, if they're trying to swipe further backwards and there's no more to go, or further forward and there's no more forward to go, we get a rubber banding animation that occurs. And you'll see through to the background of your window. So you'll need to draw the appropriate background here for when that occurs.

This is custom mode. It's a little bit more manual, so you need to go ahead and set the arranged objects yourself. If it's multi-page PDF, you know exactly what the arranged objects are. If it's a set of reminder file cards, you know exactly what that set is. And then you set the appropriate selected index. And at this point, NSPageController has enough information that it can call PageController identifier for object on the PageController delegate, in this case asking for page one.

Then you're just going to return, you don't even need to inspect the object, you can just return the same string over and over as an identifier. However, if you want to put up different views based on your objects, you can do that by returning a different identifier here.

If we look in the cache and if we don't have a view controller for that identifier in the cache, which of course we don't yet in this example, your delegate will be asked for a page controller, view controller for identifier, and you create the appropriate view controller, load the nib, and return that.

At this point, this view controller that you return does need to vend a view, and we will go ahead and ask the view controller for that view. Then your delegate will be asked, page controller, prepare view controller with object. Now, this is an optional method. View controller has a represented object property, and you can bind things to it, which works out really well. If you do not implement this method, then page controller will automatically assign the object, page one in this example, to the represented object of view controller. However, if you have more preparation that you need to do, you can do this and prepare view controller with object.

And as page controller takes a hands-off approach, and you are responsible for preparing your view, setting the represented object, if you still need to do that, whatever you need to do to prepare it. And finally, your view can draw page one. I want to put out something very interesting as a little aside here about preparing a view controller with object. You will actually be asked to prepare a view controller with a nil object.

And this is so that we can draw a blank version of your content, but still have the appropriate look of the table in the background, whatever is appropriate for this view without any specific object associated with it. And you will see why that is important in a little bit.

At this point, I just want you to realize if you are implementing page controller, prepare view controller with object, you may be asked to prepare with a nil object. We can now take this view and add it as a subview to the container view. And the user can now interact with it. At some point, the user is going to want to navigate, perhaps to page two. The delegate will be called with page controller. We will start live transition. Again, this is an optional delegate method, even in custom mode, but it may be interesting.

And then we take a snapshot of page one, and we want to start swiping to page two. We've never seen page two before. And so we need to grab some page two content. And what we do is we ask for the identifier for page two. And if we don't have a view controller in the cache yet, we ask you for a view controller. And then we can grab a view.

And we will go ahead and ask you to prepare that view controller with the page two object in this case. And then we're going to need to draw page two content. It can sometimes take a little bit of time to draw content. So we ask you to draw this content on a background thread. So it's important that you realize that your content will be drawing on a background thread in this case. And you need to be prepared for that.

But what do we do in the meantime? Well, a little bit earlier, I'd had that aside about being prepared for a nil object with your view controller. So we'll have already stashed away a blank version of a snapshot. We might have already navigated to page two at some point in time.

So we might already have a page two snapshot. But we'll still ask you to draw an updated one on a background thread. And we'll go ahead and take what snapshots we do have. We will remove the current view from the container view. And we will add in our page two content. controllers custom transition view hierarchy.

We'll put the snapshots we do have there and when you complete your drawing on the background thread we will replace that page two image whatever snapshot we're using with the most the most up-to-date image that we have. Often this happens right away the user doesn't even notice it at all by the time you start to see page two content.

Other times that if it takes a little while for you to prepare and to draw your content it'll fade in on the user and it provides a much more fluid interface for the user and it seems that swiping is much more responsive to them because it is. So the user swipes to page two and that has been completed and you get the just like before you get the page controller did in live transition and once again you need to tell page controller to complete the transition.

Often you can just do this immediately in this method and everything transitions to the next page. So you can see that the user is swiping to page two and that has been completed and you get the just like before you get the page controller did in live transition and once again you need to tell page controller to complete the transition. Often you can just do this immediately in this method and everything transitions to the next page.

to a live view and it's immediate and it's seamless. Other times if you're loading content that takes a little while just inform page controller the appropriate time to complete the transition and we remove our custom transition hierarchy view, view hierarchy and we replace it with a new prepared view controller with page two content. And that's all there really is to page controller and I want to show you another demo. This code is actually sample code that you can find off the WWDC site.

And this is the wrong project. We'll open up file cards here. There's not very much to this project. You know, there's 90 lines of code in here. Most of this is reading again, reading the contents from disk. It iterates over the objects in your documents folder. And this is just to do some binding stuff with URL because you can't just ask a name off of NSURLObject. And this draws the background. So that's all there really is to this project and what this does. It has a nice little view of the files that are in your documents folder.

And we can go in here and we can swipe. And we notice here that this looks a lot different than this one because the second file is an image. So we can see the image and I use a different identifier here and I use a different view controller. So we can swipe between them.

Of course, it's really easy to change your transition styles. So they change in the back. The buttons are just wired right up and you can even click in the table and all the animations occur automatically. It's hardly any lines of code, and you get a nice user interface.

Some notes about NSPageController, though, is if you want to have your view that is swiping inside of a split view, you can do that, but if you're in history mode, you need to be careful because split view looks at how many subviews it has, and that's how it decides how many dividers it needs. And as soon as NSPageController tries to put its private view hierarchy to do the transitioning, NSPageController will see that and add a third divider, and that's not what you want.

It's easy enough to get around. You just put a blank custom NSView there, and then you apply the view that you do want to have your content as a subview of that exactly the same size, and that's what you wire up to NSPageController, and a split view is none the wiser.

I want to remind you again that Snapshots in custom mode are drawn on the background thread, so you need to either be thread safe or take precautions. As an example, in a stable view isn't exactly thread safe when it comes to drawing. But there's an easy way of working around this, in that once you've prepared for your view that has a table in there and you've loaded your data, don't ask in a stable view for that view to reload its data while drawing is going on and you'll be fine. If the table view does start to reload data, you ask it to reload data while the drawing is occurring, I will guarantee you that it will crash.

A really interesting note, if you have the view that's attached to NSPageController, if that is set to once layers and it's layer backed or one of its parent views are set to once layers, so it's therefore layer backed, instead of taking snapshots, we still use our custom transition hierarchy to do all the animation, but we actually use live layers instead of taking snapshots because the layers aren't asked to redraw as much as they're coming in and out and we can still get good performance that way.

Let's switch gears from talking about swiping and we'll talk about Quick Look. Quick Look's been around for a while. It was a three-finger double tap and it would pull up a dictionary panel over what word you double tapped on. It is now a three-finger single tap. It's a lot easier to use now. might not realize it, but you could also trigger Quick Look previously with a hotkey, Command Control D. Of course, this is user customizable, so the user might change it. But Command Control D will perform the same gesture as a three-finger single tap.

We have a new responder method so that you can catch the Quick Look event. It's a Quick Look with event, which implies, of course, again, that we have a new event type, and we do, and it's event type Quick Look. The only valid property of a Quick Look event is location and window. So you can find out exactly where this gesture occurred.

Where's the cursor? technically modifiers is a valid property, but remember, this can be triggered with a hot key, which is likely to use a modifier, and the user can change this to something else. So modifiers isn't really useful, so only particularly look at location and window for the Quick Look gesture.

There is no corresponding event mask for this event. So you cannot add a local event monitor to catch it. You can't catch it in an override of NS application send event. You can't catch it with next event matching mask because there is no corresponding event mask. And this is just due to the way Quick Look works in that it can come in from a three-finger single tap or a hot key. And so to get things to be massaged into an appropriate event, there's no corresponding event mask. bit mass for you, so that may or may not be important to you.

We also have this other IB action method, which is interesting, Quick Look Preview Items. If the user performs the hot key gesture and the cursor is not visible, we don't know where the gesture occurred. And in that case, we'll actually call this action method. We'll find the first responder of the window, and we will go through the responder chain as normal using this IBAction method, Quick Look Preview Items.

And it's great that it's an IBAction method, because if you also have a Quick Look button somewhere in your user interface, you can just wire that up to the first responder for Quick Look Preview Items, and you don't have to do any additional work. You can rely on what's already there. there.

So if you want to do override Quick Look, you have both of these responder methods to override. If you don't implement them or you forward them up the responder chain normally, they'll eventually get to NSApp, and NSApp will route that to dictionary lookup, and the panel will come up if it's on overtext and show you the dictionary information that it's associated with that area.

There is a bug that I want to point out that once the event gets to NSWindow, it automatically gets directed over to NSApp, which means NSWindowController doesn't get a chance to see this. So you can't catch Quick Look events in NSWindowController right now, and that's important for you to know.

We'll bring back up this little demo app. And I've got a lot of text here. And we can, for example, do a three-finger single tap and let it bubble up to the dictionary lookup. And we find out that gray wolf is just another term for a timber wolf. Oh, well, that's great.

Now, dictionary has no concept about what to do with this image that I have here. It can't do a lookup. But now I can override the public API, get a Quick Look, capture the Quick Look gesture, and actually use the Quick Look API and get a Quick Look at the image.

So that was a lot. Let's talk a little bit about future proofing your application when it comes to gestures and talk about some best practices. The first thing and the most important thing you can do to future proof your application is to use the built-in implementations where possible. And as ScrollView now adopts Magnify and Smart Magnify gestures and can do a lot of the heavy lifting for you, and particularly with smart magnification and handling the human interface guidelines associated with that. So use the built-in implementations where possible.

When it comes to scrolling, you want to observe NS Clip View bounds. I've seen a lot of code where people override scroll wheel to find out how the content is scrolling. And the content can be scrolled a lot of different ways than just the scroll wheel. It might be a keyboard action or you might do it programmatically. And so they have all these catches to try and find out how the scroll view is scrolling.

You can simply observe NS view Clip View bounds changes with NS view bounds did change notification. And this is really very easy to set up. You just get the default notification center. You add observer, whatever selector you want for the name NS view bounds did change notification. And the object you want to observe is the content view of the scroll view. So scroll view content view. Now you'll be informed of the bounds changes for Clip View.

And so this makes it a lot easier to handle, for example, keeping two scroll views in sync so you can watch as the Clip View bounds change. End gestures are really interesting. As a gesture starts to occur, whether it's a magnify or rotate or whatever gesture, it might have passed through a couple of different layers in the responder chain before somebody is really taking advantage of it.

But everybody in the responder chain might need to know when the gesture actually ended so that it can clean up. So it's real important that if you get a Clip View balance change, you can see that it's not just a single change. You get an end gesture with event or you get an event with an end phase that you go ahead and forward those through the responder chain. This is particularly important for scroll wheel events, particularly if you have nested scroll views, for example. The scroll events will start getting bubbled up the responder chain and up to the parent scroll views.

And it's real important that if you get a scroll wheel event with an end phase or a cancel phase that you, if it's a local event observer, that you return that event. Instead of returning nil, you call super and you let it continue to go through the responder chain so that everybody that was watching this gesture sees that it ends or cancels and can clean up their state.

So that's it. I want to leave you with a quick summary. There was a lot that we covered in this talk. So the most important thing to know is we have API hooks for Smart Magnify and Quick Look gestures. Please use them, implement them in your application, take advantage of them.

has built-in support for magnification, particularly when it comes to smart magnify. Let NSScrollView do the heavy lifting for you. Let it worry about the HI guidelines. As our HI guidelines evolve over time, NSScrollView will automatically pick those changes up. You won't have to do anything. The appropriate change will automatically happen for you.

So again, that's gesture proofing your code, future proofing your code with gestures. We have NSPageController, swiping made easy. It's dramatically easier to do swiping now than it was in Lion. It's a great class. I suggest you check it out and where you can add swiping to your application.

For more information, you can contact our UI Frameworks evangelist, Jake. And we have his email up there. Of course, see the documentation. We have the release notes. I've checked them this morning. The updated release notes, particularly for NSPageController, are there. It's got a nice big section. And we have the developer forums. So you can ask questions there. And I will reply to those as I see them.

We have some interesting related sessions. I talked a little bit about layer backing, particularly with Page Controller. And where you can, go ahead and go layer back. We've got some nice improvements to layer back views in AppKit and Core Animation. Corbin already did this talk on Wednesday. If you missed it, I highly recommend that you rewatch it on video. If you're interested in how gestures work on iOS, coming up next in this room is building advanced gesture recognizers. This is a really great talk. So if you're interested in how gestures work on iOS, please stick around. That's it. Thank you very much. Have a good evening.