For the first post in this series, read here.
In the previous post, we examined why animated transitions can be so important in iOS and looked at some simple examples. In this post, we will continue with more complex examples.
Step 3: Keyframe Animation
So far, we’ve used very simple animations. What if we need a more elaborate animation, one which involves a series of changes happening at different times?
To accomplish such an animation, most developers would want to create two animation blocks. However, using multiple animation blocks inside our animateTransition: function can be problematic (see step 4). We can instead use iOS’s keyframe animation API, which allows us to keep all of our code inside a single animation block. Let’s take a look at our updated TTPushAnimator, which takes the old one-step animation and turns it into a two-step animation:
This animator is nearly identical to the one in the previous example with the exception of the animation APIs. Whereas the previous animator used UIView‘s animateWithDuration: function, here we’re using the animateKeyframesWithDuration: function. This new API allows us to nest keyframe calls inside of the animation block.
Each call to addKeyframeWithRelativeStartTime: adds another keyframe at the specified relative start and end times. The term ‘relative’ refers to how the start and end times are normalized, which is to say that each value ranges from 0-1 and represents a percentage of the animation’s overall duration. This relative timing concept allows us to change the animation’s total duration without having to adjust the timings of each keyframe.
Let’s see how our two-part animation looks now:
The code for this section is available in the ‘Step3_keyframeAnimation’ directory here.
Step 4: Interactivity
With our goal of providing context, a little interactivity can go a long way. Also, interactive transitions will allow the user to start a transition, then decide to cancel it after having a peek at the next scene.
As you’ll remember from the previous post, our set up in Interface Builder looks like this:
Let’s keep adding to our scene B controller. The controller will need some new properties for the interactive transition:
We’re going to delete the button in scene B, and instead use a pinch gesture to trigger the transition.
The UIPercentDrivenInteractiveTransition object that we’re using here will help us control the playback of our animation as the user interacts with it. There are some clever ways to set up our interactive transition object to make the code more reusable, but in order to make this example as simple as possible, all the logic will reside in the scene B controller. I will discuss alternative approaches in the the next post in this series.
A quick aside: The UIPercentDrivenInteractiveTransition object is the cause of our mysterious ‘only use one animation block’ issue. This helper class will only correctly manage the first animation block that appears in your animator’s animateTransition: function.
In our scene B controller, we’ll start with some changes to our initialization code:
This code will initialize the gesture recognizer and interactive transition object. Next, we need to add some logic to handle the pinch gesture:
This is the chunk of code that is responsible for mapping your gesture to the transition. We’re using the properties of the gesture object to interpret the amount of ‘completeness’ of the pinch. We can take that percentage of completion and plug it into the UIPercentDrivenInteractiveTransition object to manipulate the appearance of the transition.
The last change in the controller class is the addition of this function from the UIViewControllerAnimatedTransitioning protocol.
This function is the bit of code that tells our navigation controller to associate the UIPercentDrivenInteractiveTransition object with our TTPushAnimator object.
There’s one more issue that we can encounter with interactive transitions. Users can now cancel the transition, but our TTPushAnimator object will always apply the same changes regardless. Also, our navigation controller will always take us from scene B to scene C. We will need to update the following line in our animator objects:
This logic will fix the UIView hierarchy in the case that the user has cancelled the transition. Also, notice that the completeTransition: function is also alerted to the completion or cancellation state, which allows the navigation controller to correctly return to the original scene in the case of a cancellation.
There’s one more catch. In our previous animator implementation, we had a line that took scene B’s view out of the UIView hierarchy so we could replace it with a snapshot:
Because the pinch gesture recognizer is attached to this same view, removing the view will cause the transition to fail. Instead, we would want to keep the view in the hierarchy and hide it with a line like this:
Let’s take a look at the final product:
The code for this section is available in the ‘Step4_interaction’ directory here.
The next part in this series can be read here.