UIViewControllers Transitions

Written by MPow on Sep 29, 2014

More than one month after the first of three articles about Animated and Interactive Transitions in UIKit, I finally found the time to write the second (this) article. I will talk about something really interesting, UIViewController transitions, introcuded in iOS 7 and finally fixed in iOS 81.

A good UX is necessary to build an awesome and usable Application; Interactive Transitions and Animations, other than good looking, could help your App to be more intuitive for Users.

UX

For many years and in the 99% of the Applications all developers used the UIKit built in transitions, the popular pop and push animation of UINavigationController or UIViewController’s modal transition. There was even a little margin of customization through the property UIModalTransitionStyle modalTransitionStyle that allowed 4 differents values:

you could do something different using + (void)transitionFromView:(UIView *)fromView toView:(UIView *)toView duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options completion:(void (^)(BOOL finished))completion but even that was not enough, almost everyone used the default transitions.
Especially for UINavigationController pop something changed with iOS 7, it has built in support to pop a ViewController using a swipe from the left edge 3; this is a really natural movement that enhance the User Experience of iOS, because is more natural than search with the finger a little Back Button and is even more fast. Unfortunately the modal ViewController’s lacks this feature.
This tweet better reflect why Interactive Transitions are so important to make a good UX:

The transitionContext will provide us all the stuff necessary to build our animation, then will eventually call - (void)animationEnded:(BOOL) transitionCompleted if implemented. You can play with frames, transforms, alpha or whatever you want to build your personal custom animation.

UIPercentDrivenInteractiveTransition

Build a driven interactive transition is a bit more long, we need something that Drive an Animation and generate a Percentage of progress, a PanGesture is great for this purpose.
We should even subclass UIPercentDrivenInteractiveTransition and override its methods that will do the animation itself.
The steps of an hypothetical driven dismiss animation are the following:

Set the ViewController’s transitionDelegate

the transition delegate will provide the animationControllerForDismissedController: and interactionControllerForDismissal: (our UIPercentDrivenInteractiveTransition subclass)

Once the gesture began we call dismissViewControllerAnimated:complention: that will startInteractiveTransition:

Our gesture will generate the progress Percentage and pass it to updateInteractiveTransition:

Once finished we can cancelInteractiveTransition or finishInteractiveTransition

Example

I quickly build an example that replicate iOS 8’s Mail.app animation, is something basic, don’t use it at home.
Download ZIP
As you can see in the code, it will animate the presentation and will drive the interactive dismiss through a UIPanGestureRecognizer, fromViewController is scaled and toViewController go up or down.
Obviously we don’t want to create so simple animations, now that we have these great APIs we want to build new UIs that are not only limited to present or dismiss a ViewController, we want outstanding transitions in our Apps that enhance the UX.
Yo give you an idea I did a quick test, some months ago, that end up in that example:

Landscape

When Apple introduced transitions in iOS 7, decided to not manage the orientation of the containerView

For custom presentation transitions we setup an intermediate view between the window and the windows rootViewController’s view. This view is the containerView that you perform your animation within. Due to an implementation detail of auto-rotation on iOS, when the interface rotates we apply an affine transform to the windows rootViewController’s view and modify its bounds accordingly. Because the containerView inherits its dimensions from the window instead of the root view controller’s view, it is always in the portrait orientation.

If your presentation animation depends upon the orientation of the presenting view controller, you will need to detect the presenting view controller’s orientation and modify your animation appropriately. The system will apply the correct transform to the incoming view controller but you’re animator need to configure the frame of the incoming view controller.

That was really annoying for Developers that needed to build custom transitions on iPad or on an iPhone App that allowed rotation. Fortunately in iOS 8 this is changed and we don’t need workarounds anymore. However if you still need to support iOS 7 you will need to manage it, some people calculate the frames based on the rotation, but this is long, annoying and may break easily especially when you even need to transform your Views.
My solution was to place a new UIWindow on the top on the animation’s containerView during the animation and use the rootViewController’s view as containerView; since that view rotate properly my ViewController’s views can rotate with it, and I don’t need to change half of the code every time I want to change something in the animation.