Direct onscreen manipulation is the cornerstone of the user experience on iOS. iOS 10 includes new support for making onscreen interactions even more immersive and interactive. Dive straight into the philosophy and techniques of building completely interactive, interruptible animations in your apps.
My name is Bruce Nilo.
I'm an Engineering Manager for UIKit.
And today, my coworker, Mike Turner, and I are really excited
to talk to you about some new animation APIs
that we're introducing to UIKit in iOS 10.
These APIs we're confident are going to make it even easier
for you to create natural, and smooth-feeling,
and responsive applications.
So we're going to begin our talk today with a quick refresher
of the existing implicit animation APIs
that exist in UIKit.
We're then going to dive into a brand new class
that we are introducing called UIViewPropertyAnimator.
We're going to talk about some extensions we've made
to view controller transitioning
so that you can take advantage of this new class.
Mike is going to come up and give a great demo
that uses these new APIs
and a brand new sample Photos application that you'll be able
to download in the near future.
We're then going to talk about a few subtle issues related
to touch processing and hit testing for this new class
and how you can use this new class
to create even interruptible keyframe animations.
So, implicit property animations in UIKit basically are there
to -- well, they exist in a way that allows you
to create a dynamic context
by which you can update the properties of a view
that can be animated over time.
It's an implicit API because UIKit creates the animation
on your behalf.
It notes when the value changes and uses the original value
and the end value to interpolate the value over time based
on the duration that you specify with an optional timing function
that specifies the pacing of the animation.
So let's make this a little concrete.
Here's a very simplified animation.
We're going to use this throughout the presentation.
We have a circle that's basically going
from the left to the right.
It's the center property of that view that's animating.
And when in this graph we see a dotted circle
or a dotted square, it means that's the value
that you're actually seeing animate.
Whereas the solid shape is going to indicate the target value.
And we sometimes call that the model value
versus the presentation value.
So we start the animation, and it proceeds a pace.
In fact, you'll notice that the pacing was uniform.
And the question is - how do we do that today?
Well, today it's really easy.
Most of you are probably familiar
with the animateWithDuration API.
And in this case, we're specifying that the x coordinate
of the center of that circle should move from 0 to 100.
Now the timing function that was specified there was .linear,
which basically means it was the identity function.
Now a quick review of what a timing function is in UIKit.
It's basically a cubic function that maps "0 to 1" to "0 to 1"
with a couple of constraints.
Namely, that at the start of the animation --
meaning when you just started --
the progress of the value that you're animating is
in fact the start value.
And at the end, it's the end value.
Now timing functions that are not identity pace
And it's really easy to see this
with this exaggerated ease-in timing function.
You'll notice that, halfway through the animation,
the progress has barely moved.
It's really chugging along pretty slowly.
We're almost 90% of the way done.
We're still only halfway there.
It's in the last 10% of the time that you specified
that we're going to make up the rest of the distance.
Basically, this thing is accelerating
from a very slow pace to a high pace towards the end.
So that's what timing functions give you.
UIKit currently really only gives you four timing functions.
These are specific instances of cubic Bézier functions.
There's the identity function we saw before.
There is .easeIn.
And finally, there's .easeOut.
And these are very subtle functions that we give you
to kind of affect the feel of your animations.
Now what about springs?
We also have an implicit animation API
where you can specify a spring.
And you wouldn't be wrong if you said, "Well,
that's not really interpolating.
We're kind of overshooting the value
and kind of bouncing back."
However, in what follows, I'm going to encourage us all
to think about the spring as another type of timing function.
And let's see why we can do that.
Let's look at this simple application
where we're animating a square from left to right.
And we're graphing effectively the position of the square
as it proceeds over time.
And it kind of looks like a timing function.
The thing is it's not a cubic.
It overshoots its values, but we can still think of it
as affecting the pacing of the animation.
And in the APIs that follow, that's how we are going
to classify spring animations.
Now there is another important edition that was made
to animations in iOS 8.
And I want to talk to you about them because we're going to look
at them again a little bit later.
Basically, consider an animation that changes
in the midst of the animation.
So you're going from one position to the other.
And halfway through, you change it.
Now prior to iOS 8, if you did that,
there would be a discontinuity
in the animation unless you specified a special option --
And if you did that, it would look a little bit better.
There wouldn't be a discontinuity or a jump,
but it wouldn't be smooth either.
The velocity changes rather abruptly.
In iOS 8, we made a significant change where animations
for certain properties were done additively.
We didn't remove the previous animation, we added to it.
And then that smoothed out the change in velocity.
With the new UIViewPropertyAnimator,
it's going to be impossible to get into the first situation.
OK, let's get to the new class now
that we have done the quick refresh.
Some of its features.
It's really familiar.
If you're familiar with the existing animateWithDuration
suite of APIs, you'll be right at home with this new API.
That means you can pause and stop the animations.
You can scrub them, meaning you can move forward-to-back as you
like based on a programmatic API.
You can reverse them.
You can say, "Never mind.
We're going to introduce a whole new plethora
of timing functions, not just the four that we had before
with one kind of small subset of spring animations.
And finally, when the animations are running,
it'll be really easy to add to the running animations.
So it's kind of hard to talk about API, and I don't want
to do it by going into kind of a header crawl.
So let's get an overview of what this API looks like.
At the center of it all is this new class --
It's implemented in terms of two new protocols.
And the reason for implementing them as protocols or conforming
to two new protocols we'll see is really powerful
when we couple it with view controller transitions.
When you create a property animator, you're going
to create a new type of object
which is effectively specifying the timing function
that you want that animation to use.
We provide two new concrete classes for these.
We've introduced a couple of new enumerations that are used
by the methods defined in these protocols.
For example, the state
of an animator can be inactive, active, or stopped.
And we're going to be talking about that momentarily.
We also introduced a position, which basically is
where did the animation finish.
Did it go all the way to the target values that we specified?
Did we reverse it and go back to the beginning?
Or maybe we interrupted it and we're somewhere else.
OK. We're going to start focusing
on the center part of this graph.
We're then going to talk a little bit
about all these new timing functions.
So let's look at kind of the corner piece of the API
which is defined by the UIViewAnimating protocol.
This is where all the fun is.
It's really simple actually.
There's a couple of properties that you can observe and set.
For example, you can set the fractionComplete.
You can tell an animation to start.
You can pause it, stop it, finish it.
It's kind of self-explanatory.
The next protocol adds the implicit characteristics
to this animator.
It's where you add blocks where you set the target values
of the properties that you wish to animate.
There's also an interesting method called continueAnimation
which allows you to proceed from a paused animation
with a completely different finish duration,
and possibly even a different timing function.
UIViewPropertyAnimator has a couple of constructers.
It also has some properties that control the touch handling
that we're going to get into at the end of this talk.
So we're going to talk first about how to use this -- basics.
We're going to talk about some details that perhaps are not
so obvious that you might encounter when you pause
and scrub an animation.
There's also some interesting possibilities in terms
of how you can reverse an animation.
Finally, we're going
to introduce all the timing providers.
So let's start a really simple animation.
And I've made this one a little bit more interesting
for pedagogical reasons that you'll see soon.
And let's ask the question,
"How would you use the property animator to do this?"
And it's really, really easy.
The first thing you do is you create a timing object.
This is just like the .easeInOut enumerations you've used
in the existing API.
You instantiate one of these objects by specifying a duration
and the timing object.
You add animations in terms of closures,
which are updating properties that you want to animate.
You can add an optional completion.
In this case, that completion is changing the color back
And then you can start it whenever you want to.
OK, it's kind of verbose compared to the old API,
but now you have an object and there's all kind
of things you can do with it.
It makes a world of difference.
So let's look at some of the properties that are available
There's three that are quite interesting.
There's the state, there's whether it's running or not,
and whether it is running in the forward or reverse direction.
These are observable properties in the KVO sense of the word.
Now the first thing you do is you add an animation.
We're going to animate the circle again from left to right.
Once you do and you've added an animation closure,
you can start the animation.
The second you start the animation,
the state changes, it starts running.
The values of the actual views change now.
There is target value
and there's what you see on the screen.
And then the animation starts.
Now what's different now is I can pause
that animation halfway through.
And to do that, I just tell the animator to pause.
I can tell it that I want it to continue again
in the opposite direction.
I'm still paused when I do, and you'll notice
that the states change.
And then I can start it, and it's going
to start going backwards.
And then I can change it again, and it's going to go forwards.
And when it completes,
we're going to call any completion handlers
that you've registered.
And in this time, we're calling it with a .end position
because we made it all the way to the initial target.
If we hadn't reversed it again while it was running,
we would have called the completion with a .start.
That way, your completion handler knows
where the animation actually ended.
OK. Instead of pausing an animation,
there's something really neat you can do.
You can stop the animation.
Now what does that mean?
Well, the first thing you'll notice is that, all of a sudden,
that circle became solid in the center.
We've effectively promoted what you're seeing on the screen
to the actual model value that's in the view that's animating.
At that point, you can do anything you want
with that circle.
But we passed in a false parameter to stop,
which means don't jump immediately
to the inactive states, stay in this quasi-finish state.
And what that means is that, at some point in the future,
you're going to call finishAnimation and you're going
to specify a position where the animation is in fact finishing.
In this case, we're not finishing neither
at the end or at the beginning.
And you're completion handlers will be called
with that position.
Now if you called stopAnimation with a true parameter,
we wouldn't even call your completion handler.
It's just finished right then and there.
Now let's look at what happens
if you call finishWithADifferentPosition.
Let's say we call finishWithTheEnd.
It immediately jumps to where the animation was
Now you might think, "Why would I want to do that?"
And it actually gives you a lot of possibilities.
For example, imagine you're pausing this circle
or you're stopping this circle when you touch it,
and then you're dragging the circle around.
And maybe you set up some UIKit snap behaviors
in the end position and the target position.
And when you release it, based on the velocity of your gesture,
it may snap into those relative positions.
And once it reaches that, you can then call finishAnimation
with a .end or .begin.
And so you can do some interesting things
to finalize the animations that you've started.
OK, pausing and scrubbing.
Not much there, right?
Well, it's kind of interesting.
So there's a sample app here which we are going to --
where we set some gesture recognizers on the square and on
that little green progress bar.
And we can scrub that progress bar.
We're updating where the square is.
And then we can continue the square.
And you'll notice that kind of the position over time --
there's kind of a weird protuberance in that graph,
which we're going to talk to in a bit.
Before we do that, let's look
at what these gesture recognizers look like.
On the square, we added a tap gesture recognizer
which checks whether or not the animation is active.
And if it is, it checks is it running or not running.
And if it's running, it's going to pause it.
And if it's not, it's going to start it up again.
On the progress bar, we set up a pan gesture recognizer.
And we're going to just compute a fraction based on the position
where we are in the pan relative
to the full bounds of that progress bar.
And based on that fraction that we've computed,
we're going to update both the animator
and the progress bar as fractionComplete.
OK. So, what's going on with this graph?
There's actually something interesting going on here.
And to explain what, I'm going to go back to this kind
of exaggerated ease-in curve again.
So let's say this time, 50% of the way in,
we pause the animation and now we want to scrub it.
The important thing to note is that the fractionComplete
that you're actually setting has nothing to do with time.
We've just paused the animation.
What you're really setting is the fraction
of progress towards the end value.
And in this case, we've hardly made any progress at all.
Now we're going to scrub that line and we're going
to want to continue it again.
We don't want to jump the perceived position
of the view that we're animating.
So, in fact, what happens is we kind of map time back
to the timing function of the original animation.
Which means when we continue in this particular case,
the animation is going to finish really quickly.
So when we go back to the curve that we actually saw before,
you'll notice that we're kind of jumping back
onto the timing function -- that easeInOut function.
And it's important to note that when you're pausing
and scrubbing, depending
on whatever UI affordance you may be using to do this,
you may see these discontinuities and have to kind
of compensate for them.
OK, let's talk a little bit about reversing.
There's three ways you can reverse.
One is you can pause reverse -- kind of interact with something
and start it up again.
And you'll notice that you're literally reversing right back
through the timing function that you've specified.
You can also reverse on the fly.
Which means that, as the animation is running,
you can tell it to reverse.
And that's kind of like hitting a brick wall.
Again, there's going to be this huge discontinuity in velocity.
And that might be what you want if you want to kind
of represent, say, a perfectly-inelastic collision
or something like that.
But if you don't want that and you want it to be smoother,
you can animate additively.
And in this case, we will reverse the animation not
by changing the reverse property, but we're going
to actually change the values back to the original values.
Now what's different about this is
that when your completion handlers are called,
they're going to be called with a position of .end.
You've basically changed the target.
You're no longer going towards the original target.
You're going back towards the target you just specified.
OK, there's some interesting timing objects
that we've made available to you.
The first is the UICubicTimingParameter class.
If you create this class with no parameters, you're going
to get the default core animation timing curve.
This was previously unavailable at the UIKit level.
The second variant
of the constructor is essentially the existing canned
And finally, we opened up all
of the cubic Bézier curves available
on the unit square to you.
And as an example of something that you might do --
this particular Bézier curve, if I were to give it a name,
it would be "speed in, speed out".
And you can kind of go wild with the Bézier curves you want
to create for timing functions.
We've given you more ways to animate like a spring.
And, again, consider that we consider springs now
to be influencing the timing.
If you create a UISpringTimingParameter object
with no arguments, you're going
to get a critically-damped spring animation,
which many of you have asked for.
It's one that we use, for example, when we push or pop
onto a navigation controller.
The second variant is very similar
to the existing UIKit spring animation API
with a small difference that I'm going to get to in a second.
But before we get to that, we've opened
up basically the spring equation to you.
You can specify any coefficients you want,
and we will effectively honor those.
There's a point here though, and that is that the duration now
that you specify is ignored.
And we're computing that duration based on the solution
of the spring equation.
You're going to see an example of that.
I said there was a difference.
You'll notice that the initial velocity is now a vector.
It's not a scaler.
Most of the time, we're actually not going to look
at the y component of this vector.
However, if you are animating the center of a view,
we're actually going to look at both the x
and y components of that vector.
And to see why, look at this example application
where we drag a square off the center.
And when we release it, we're going to spring back
to the center based on the velocity of the gesture.
Now, up until now, the velocity was always along the line
connecting those two squares, which was kind of unfortunate
because my gesture might be released anywhere along the
plane, and we effectively really weren't taking
that into account.
However, now we are.
So let's look at this little video
which shows how we now will spring back to the center
and take into account the two-dimensional vector position.
You'll notice that, when we go off,
we're going to take both the x and y components.
It's really easy to do that.
OK, we have this great property animator.
And guess what?
I want to take advantage of it
for custom view controller transitions.
I want them to be interruptible, too.
Now, three years ago or so, I gave a whole talk on this,
and so it's a little bit complicated.
So if you're not familiar
with custom view controller transitions,
please refer to that talk.
But I'm going to give a quick run-through to set the stage
of how we've extended these protocols.
View controller transitions basically are a bunch
of interlocking protocols --
two of which interactive transitioning
and animated transitioning are protocols
that you create objects which you conform to --
well, the objects that you create conform to.
The system will create another object that gets passed
into the methods of that protocol.
And this will become clear in a second.
But let's remember why we want to do this.
Imagine you have an app with a navigation controller
and the pop kind of looks OK.
It's that great,
critically-damped spring animation.
But, you know, your app wants something else.
Your app wants to really kind of have a different look --
something that explodes out maybe
with a blur effect underneath.
That's what custom view controller transitions let
And moreover, it lets you do those
and drive those interactively.
OK. The way we get the objects that conform
to these protocols are just via a delegate.
It might be the navigation controller delegate,
or it might be a view controller's
And we're going to ask that delegate, "Do you have an object
that conforms to animated transitioning whenever you do a
present, or a push, or a pop?
And if you do, we're going to bypass the built-in transition.
We're going to create one
of these context transitioning objects
which give you all the information you need
to actually animate a transition.
And we're going to call animateTransition,
passing in that context to you."
Now how do we make it interruptible?
Well, what we've done is we've added a new optional method
And if you implement that, you're going to return an object
that conforms to UIViewImplicitlyAnimating.
Now this could be a UIViewPropertyAnimator,
but it doesn't need to be.
It could be, for example, another type of animator that,
say, you implement in terms of UIKit dynamics
or some other animation strategy.
If you do not implement the interaction controller,
meaning you only implement a custom animation controller,
then you need to implement animateTransition.
And you would do so very simply, like this method.
You take the interruptible animator that you would return
and you would basically tell it to start.
And that's really all you need to do
to implement animateTransition.
However, we kind of advise
that you use an interaction controller if you're going
to make it interruptible.
And, again, the first thing you have to do is conform to
or return an object that conforms
to animated transitioning.
We will then ask you for an object that conforms
to interactive transitioning.
And we're going to pass the object that you return back
to us previously as a parameter.
Often times, you want that interaction controller
to actually drive the animation controller.
If you do, we're not going to call animateTransition.
We're going to call startInteractiveTransition
and we're going to pass in the same context
that we would have passed into animateTransition.
We provide a concrete class that you can use
that makes this really easy.
Now there was a restriction before
where if you implemented UIPercentDriven
you actually needed the animateTransition method
of the animation controller to have been implemented in terms
of the existing UIKit animation APIs.
But now with the interruptible animator, UIPercentDriven
InteractiveTransition actually doesn't care about that.
It's just using the protocol that we've defined
And that's it.
So you could actually have a completely different animation
that's driven by UIPercentDriven
InteractiveTransition, which is kind of cool.
OK, let's say you already have one of these things,
you already have a custom transition.
How do you migrate?
Well, one way to do it would literally be rename your
animateTransition method to myAnimateTransition
and build your interruptible animator in such a way
that you just add that method call into one
of the animations of the animator.
Not too difficult.
We did have to extend some other objects in these protocols.
For example, context transitioning now has
That's how you enter into the interactive state now.
Prior to this, you started interactive
and you ended non-interactive.
Now, you can move back and forth.
And the way you do that is
by calling pauseInteractive, or finish, or cancel.
Also, the property values isInteractive
and transitionWasCancelled can now change as you move back
and forth between an interactive and non-interactive transition.
We added a variable called wantsInteractiveStart.
Because now that you can move and forth,
maybe you want your interaction controller to start
And this variable controls that behavior.
Finally, we have UIPercentDriven
There are a few rules.
interruptibleAnimator -- if you implement it,
we expect it to actually be there.
Meaning don't do something else.
The system might get grumpy.
We're always going to call animateTransition
and startInteractiveTransition first.
So that would be a great place
to create your interruptible animator because we are going
to be calling it subsequently with the same context,
and we expect the same instance of animator
to always be returned.
Finally, the animator survives the life of the transition.
It shouldn't become inactive
until the entire transition is over.
Now, Mike is going to come up and show you how
to use these APIs in a real app.
I didn't give you much time.
So, let's start with a demo application here.
We've got an application
that utilizes the UIViewPropertyAnimator
and the additions to UIViewController transitioning
to create a nice interruptible custom transition
in a basic app.
So, first, we have an application
that uses a UINavigationController
and has a Collection View Controller inside of it.
And we have this grid of photos we can scroll through.
When we tap one of the photos,
it will push another view controller on the navstack.
And that kind of shows a detailed view
of your photos here.
And if we hit the Back button, we'll pop that off the navstack.
And alternatively, we can slide from the left edge of the screen
to do an interactive transition back.
And this is all available basically for free
And as Bruce just described, we also have a rich set of APIs
that allow you to customize that transition.
And, here, if we want the photo to kind of zoom up out
of the page, we can do that, which is pretty cool.
When we hit the Back button, it'll zoom back down.
And this was all possible before.
In addition to that, you could pull
down to start the transition interactively, move around,
and then let it finish in an animated fashion.
So I want to show you how we've accomplished
that using UIViewPropertyAnimator.
OK, so the first thing you need to do is tell the system --
in this case, the navigation controller --
that you're going to be providing a custom transition.
So, here, we're going to conform
to the navigation controller's delegate, and we'll do
so with an AssetTransitionController.
And that's just an object we've created that'll implement these
So, here, we have two methods that are pretty important.
The first is calling us for an animation controller
with a particular operation -- a push or a pop.
We'll save that operation and we'll return ourselves
since we're going to be the animation controller.
And then when we return an object from that API,
the system will call us for an interaction controller,
at which point we'll also return ourselves because we're going
to be the interaction controller as well.
And once we've done that, the system knows that this is going
to be an interactive transition.
So let's look at UIViewController
Here, the system is going to call startInteractiveTransition
on our object with a transition context,
and that'll have all the pertinent information
that you'll need to start your custom transition.
And at this point, we're going to create a helper object here
that we'll see a little bit more of in a moment.
But that's the object that's going
to create our transition animator.
And we're going to pass the context to the operation
that we saved
and a panGestureRecognizer we were using to start
that transition interactively.
In iOS 10, we also allow you to start an interactive transition
in two phases -- either an animation phase
or the interactive phase.
So if we did start from a panGestureRecognizer,
we're going to set this initiallyInteractive ivar here
to "true" to let the system know
that we're starting this interactive
And next, let's look
at the animated transitioning implementation.
Here, we're not really concerned about the animation methods.
As Bruce mentioned, we're using an interaction controller,
and that's going to call startInteractiveTransition
as opposed to animateTransition.
But the new interruptible animator API
in iOS 10 is what we're really interested in here.
And, here, our helper object that we'll see
in just a second creates a UIViewPropertyAnimator
and returns that for us to the system.
So, that's where the system is going
to add any alongside animations.
And in this demo, you'll see the navigation bar at the top kind
of animating along with our transition.
The system is going to take advantage of this animator
to add those animations.
So let's look at the transition driver helper object
Here, you saw that we initialized this object
And that's going to call through -- excuse me.
There we go.
That's going to call through to this helper method
with some animations, and it's going to set up some
of the background animations for the transition.
You saw visual effect was animating.
And the two view controllers, alpha, was kind of animating.
We'll talk about the image changing its frame a little
And it'll pass in a completion closure as well to help clean
up some of those background views.
But, here, let's focus on how we create that property animator
that we returned to the interruptibleAnimator API.
So we first get a transition duration from a helper function,
and we'll look at that in a second as well.
And we're going to create this PropertyAnimator with a duration
and a curve of .easeOut.
And we'll pass in those animations that we were given.
And we'll create a completion handler here,
and we'll call those completions that were passed to us.
But the important thing here is we're going to call
to the transition context when this animator ends
to inform the system that, "Hey,
we're done with this transition."
And we can get where we're --
the completionTransition API takes a bool
that we can get the value that we're supposed to pass through.
So we pass "yes" to that API.
That means we're going to end the transition.
If we pass "no", it means we're going to cancel the transition.
So before I go a little bit further
in the transition animator helper object here,
I want to show you a few more features in the demo app
that are made possible by using UIViewPropertyAnimator.
So if we go back to the demo, I showed you that you can zoom
out of the screen and zoom back in hitting the Back button.
But what I didn't show you is you can pause now midflight.
So we're in the animation phase, we pause the animation,
and we move into an interactive phase.
So we're interacting with this photo
in the middle of this transition.
When we release our finger, they'll kind of animate again,
and then we can interrupt it again.
So we're free to move back and forth between an animation phase
and an interactive phase of the transition.
So it's really pretty cool.
So before I go back to the code and show how we've done
that using the new view controller APIs,
I want to describe a particular scenario here that'll help us
understand what the code is going to be doing.
So imagine we're starting.
We're pulling down with the panGestureRecognizer here,
starting an interactive transition.
And then we're going to release our finger from the screen
and start an animation phase, and while it's animating,
we're going to pause midflight and we're going
to interact with it again.
And we're going to cancel that transition
by pulling it back up.
So that's the scenario that we're going to describe
when we look at this next piece of code.
So I've broken that down in this sample application here
in terms of four methods.
The updateInteraction, which is the gesture recognizer handler
that's going to update the interactive transition.
And when our finger lifts,
we're going to call the endInteraction function.
And then we're going to call the animate function to animate
to either the begin or end position.
And then when we interrupt that animation,
we'll end up calling pauseAnimation.
So we can do this loop a few times
in the lifetime of a transition.
We can actually do it as many times as we'd like.
But in this scenario, we did it twice.
So we look at updateInteraction.
This is a gesture recognizer handler.
And when it's called in "state begin" or "state change",
we're going to look at the translation
of the gesture recognizer and we're going to use
that to compute a progress step that we're going to add
to the transition animator as fractionComplete.
And then to scrub those animations in the background --
you saw the navbar in some of our chrome, the visual effect
in the background -- it's as simple
as setting the fractionComplete on this transition animator.
And that'll let us scrub through all these animations basically
And then we'll call to the transitionContext
to update the interactive transitions percentComplete.
And then, finally, we have
that image that's moving around screen.
When we're in the interactive phase of this transition,
we're going to handle setting that frame manually.
So we have a little helper function here to do that.
And finally, we'll reset the translation
on this gesture recognizer.
So when we're called, when the finger lifts off the screen,
we'll get a "state ended" on the gesture recognizer
and we'll call endInteraction.
If we look at endInteraction,
we first just check is our transition context interactive.
And we want to make sure it is before we move it
out of the interactive phase.
And we'll call a little helper function
that tells us whether we're going to complete
in the begin position of the animation or the end position.
If we're completing in the end position, then we're going
to call to the context to say, "Hey,
we're finishing the interactive transition here.
We're going to move to an animation phase."
If we're called with the begin position,
we're going to say we're cancelling the interactive phase
and we're going to move to the animated phase.
And then we just animate.
So let's look at the animation method here.
So, previously, I told you
that the transition animator animates kind
of the background chrome and the alpha transition.
But, here, we're going to create a second propertyAnimator.
And we're going to use that to animate the frames
of these images that are moving around screen.
And the reason we do that is we might want those animations
to have a different timing function
than the animations in the background.
And we'll see that in just a moment.
So, here, we're just going to add animations to that.
Now we're going to specify the end position,
or basically the target or the initial frame of the image.
And then we're going to start
that property animator and keep track of it.
Now, remember, we have this other transition animator that's
living the lifetime of this transition
that we gave back to the system.
And we just need to make sure that that's animating
in the correct direction.
So if we specified a toPosition of "start", then we need
to reverse that animator.
And finally, if this is the first time that we're calling
through animate here, our transition animator is going
to be in an inactive state.
So all we need to do is start that transition animator
and we're off to the animation phase.
Alternatively, if it's been started and subsequently paused,
we're going to use something a little different.
We're going to call in to continueAnimation
(withTimingParameters and a durationFactor.
So continueAnimation lets you continue a paused animation
with different timing parameters and in different duration,
and the remaining duration if you'd like.
And here, we're going to pass "nil", indicating we'd
like to use the timing parameters that were passed
when we initialized this transition animator.
But we're going to change the durationFactor
of the continued animation.
And that will allow us to synchronize the durations
of this new itemFrameAnimator we just created
above with the duration remaining
of this transition animator.
So the durationFactor is just defined
as 1.0 equals the initial duration that was specified
when you created this transition animator.
And that's all we have to do to kick off the animation phase.
And lastly for this cycle,
let's look at the pauseAnimation step here.
And this is when it's animating and we paused.
We put our finger down on the image view.
And we just put a gesture recognizer
on that image view in the sample app.
And we're free to do that now
because UIViewPropertyAnimator will hit test the animating
So on that itemFrameAnimator we just created above,
we're going to stop that entirely with a parameter
of "true", indicating to the system that we have no intention
on continuing this animation,
that we'd like the model values written directly --
excuse me, the presentation values written directly back
to the model.
And then on the transition animator, we're just going
to call to pauseAnimation.
Because as we saw in updateInteraction,
we were scrubbing that animation
when we were interacting with the image.
And then, finally, we'll indicate
to the transition context that we're moving
out of the animation phase back to the interactive phase.
And so I'd like to show you one more tip and trick,
kind of a little detail of the demo here.
We saw that this timing is kind of slow, and I've done
that for illustration purposes.
But you might want to make that more natural-feeling
and a little bit more lively.
So if we look at changing that timing to a spring,
it kind of jumps out of the page
and only the image is springing into its position.
And here, those background animations are continuing
to operate on that .easeOut curve that we specified.
So we were able to do this change
and make this feel much more lively with really one line
of code, and we can still interact uninterrupted.
So let's take a look at that line of code really quick.
So we come back to our demo and we look
at our propertyAnimator convenience method.
We'll notice that we're specifying a property animator
with timing parameters that are linear here.
And that's what gives us this mechanical feeling.
And a colleague of mine left this great comment
in here specifying some SpringTimingParameters
to use instead with a great mass, stiffness,
damping, initial velocity.
And that fully specifies this spring.
So when we create the propertyAnimator down here,
this duration that we're passing in actually isn't used.
Rather, it'll be computed based
on the properties we've provided in the spring timing.
And because of that, we have animation duration helper
function here that just uses the propertyAnimator API
that we just saw down below here to compute the duration for us.
So it basically solves that spring equation for us.
And we can use that to match the other duration
of the transition animator.
That's all super simple.
I know this code is looking a little bit complex,
but it's really only a few hundred lines of code.
And UIViewPropertyAnimator made it all possible.
So, with that, I'd like to give it back over to Bruce.
OK, I've got to speed things
up because we're a little bit over time.
We need to talk a little bit about hit testing.
We're going to assume that UserInteractionEnabled is "true"
so that we can actually hit test our views.
If it was "false", we would just be swallowing all touches.
We have this property called isManualHitTestingEnabled
that defaults to "false".
And the reason it defaults to "false" is because we want
to be able to hit test moving views.
If it were "true", which is the current behavior of the system,
when you try to touch down on the position of the view
that you saw, we wouldn't hit test.
And perhaps, puzzlingly, it would hit test where it's going.
We don't want that to happen
for an interruptible property animator.
Now in this talk below, which I recommend that you see,
a technique was given whereby you could override hit test,
do some calculation to convert to the presentation layer
and call "super" so that you could hit test a moving view.
That technique still works.
But now with propertyAnimators if you have a moving view,
by default, with manual hit testing enabled to "false",
we're actually going to hit test against that view.
So that's pretty cool, and it's really easy to do.
In fact, that's how everything we've been looking at on stage
so far has been working.
Now this doesn't work all the time.
If you have deep view hierarchies and so forth,
we are not going to do the right thing.
So you may need to use the other technique talked
about in the other talk.
OK. We have this whole other piece of API
for keyframe animations that exist in UIKit today.
How do you make those interruptible?
To recall, a keyframe animation is basically kind
of like a regular animation except you specify the path
that you want to animate from.
We want to make those interruptible as well.
In fact, we want to do something like what we're seeing
on this video right now.
We want to pause it, scrub it.
And I guess it's not too surprising.
In fact, you can do it.
It's really easy to do.
You take the existing API and you add it as a closure
to an animator's animations.
And with that, you have interruptible keyframe
There's one last thing.
I kind of lied.
That's the built-in navigation pop.
Currently, if you have an interaction controller,
you can't use that.
But new in iOS 10, if you put a failure requirement
on that gesture recognizer or your own,
we will allow the built-in pop gesture recognizer to recognize
and use the built-in navigation transition alongside your
So, in summary, we've learned how
to create interruptible animations
with a UIViewPropertyAnimator.
There's a whole new range
of timing functions available that you can use.
You can use interruptible propertyAnimators
to create interruptible view controller transitions.
For more information, go to this URL.
There's a couple of other sessions in UIKit that talk
about other types of animations, two of which are tomorrow.
I encourage you to go see those.
Looking for something specific? Enter a topic above and jump straight to the good stuff.
An error occurred when submitting your query. Please check your Internet connection and try again.