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.
- UIView Interactive Animations
- UIViewController Transitions
- UICollectionView Transitions 2
- Cell animations
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
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.
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:
WHY CANT I JUST USE A SWIPE TO GET BACK https://t.co/oUTIupNwjM— Sentry (@Sentry_NC) 28 Settembre 2014
Usually, building custom transitions, can be a good pratice present a Modal ViewController animated and allow an interactive transiton to dismiss it, as we can see in Mail.app on iOS 8
Apple introduced some new APIs in iOS 7 to make developer’s life easier, one useful for Animated transitions, the protocol
UIViewControllerAnimatedTransitioning, and one for interactive transitions, the class
To be able to start an animated or interactive transition we have to tell to the UIViewController who will coordinate his transition, to do that we have to set the
id <UIViewControllerTransitioningDelegate> transitioningDelegate, the delegate will be in charge of provide the object that will coordinate the whole transition:
- The animated presentation
- (id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source
- The animated dismiss
- (id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed
- The interactive presentation
- (id <UIViewControllerInteractiveTransitioning>)interactionControllerForPresentation:(id <UIViewControllerAnimatedTransitioning>)animator
- The interactive dismiss
- (id <UIViewControllerInteractiveTransitioning>)interactionControllerForDismissal:(id <UIViewControllerAnimatedTransitioning>)animator
nil will end up using the default transition.
To build animated transition we just need to pass an object conforming to
UIViewControllerAnimatedTransitioning , its methods are pretty easy and to build a basic animated transition we can just do:
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.
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
- Once the gesture began we call
- Our gesture will generate the progress Percentage and pass it to
- Once finished we can
The core of the animation will be something like:
I quickly build an example that replicate iOS 8’s Mail.app animation, is something basic, don’t use it at home.
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:
When Apple introduced transitions in iOS 7, decided to not manage the orientation of the
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.
UICollectionView Transitions has not been written yet. ↩