View Controller Transitions

Blog Logo

Selim Bakdemir

on
26 Apr 2016

read

View Controller Transitions

Prior to iOS7, transitions between view controllers was not supported by SDK and we were able to create those transitions via custom UIViewAnimations. Beside this, making those transitions interactive was another painful task.

Now we have a nice API for controlling the animation between view controllers. The API heavily consists of delegate pattern and protocols. The generic work principle of the new API can be explained as:

Those animation controllers which conform <UIViewControllerAnimatedTransitioning> protocol are the ones we will use for the custom transitions. To conform <UIViewControllerAnimatedTransitioning> protocol, animation controllers should implement the following methods:

The key point here; UIKit sends to our animation controller a transitionContext object, in which we can find any information about the transition to happen. We are going to use transitionContext object to fetch views of both presenting and presented view controllers, calculating final frames, managing lifecycle of transtiion etc. For more details see transitionContext.

Working Example

We will implement this complicated and protocol oriented API, by creating a simple fadein-fadeout transition between 2 view controllers.
Let’s assume we have 2 view controllers, HomeViewController and FadeViewController respectively.

For simplicity, HomeViewController would present a FadeViewController instance. Nothing new here except the transitioningDelegate:

UIKit asks our transitionDelegate, which is FadeViewController in our case, whether there is an animationController for preseting this voew controller. We return self.presentAnimationController for presenting transition, and nil for dismissing transition. We do not want to have any custom transition while dismissing FadeViewController.

But what is the self.presentAnimationController property? Where is it initialized?

Actually the property self.presentAnimationController is marked as IBOutlet. This means we can specify that property from Storyboard or Interface Builder.

First we create our animation controller. Here is the controller FadeTransition.h:

As i told before, UIKit send (id<UIViewControllerContextTransitioning>) transitionContext object to our FadeTransition object, therefore we are able to fetch the necessary metadata for the transition. Lets cover it step by step. First we get the 2 views of view controllers:

Then we get the containerView, which was created by UIKit itself temporarily.

UIView*containerView=[transitionContextcontainerView];

containerView is active and being displayed during the transition. As soon as we finish or cancel the transition, containerView is destroyed by UIKit. We can think containerView as a scene, in which we play any animation we want, and close the scene when the transition ends.

The only critical point is here, when we call [transitionContext completeTransition:YES],
we tell UIKit that our transition ended. Within that call, UIKit calls the necessary lifecycle methods on both view controllers such as -viewDidDisappear: on HomeViewController, and -viewDidAppear: on FadeViewController.

Here is how i specify the FadeTransition object in Storyboard:

This is how it looks to present using FadeTransition:

Here comes the interesting part. We will use the same transition to dismiss the FadeViewController.

First, we have our ordinary presenting and dismissing animation controllers. Note that we already set the id <UIViewControllerAnimatedTransitioning> presentingTransitionDelegate to FadeTransition in Storyboard.

The dismiss transition however, we choose to set it in code, in -viewDidLoad method:

First we will get the views of view controllers as always. We also know that the source view controller is a PiecesViewController, therefore we will get the coloured views with their tags.

After that we calculate the frames for each subview and animate to their final position

When the animation finishes, we call completeTransition: to inform UIKit to complete the lifecycle of the view controller.

When we build and run the project, we will see the new PieceTransition, but we immediately aware of that the transition is not interactive. We can only dismiss the view controller by clicking « Dismiss button down.

Making the transition interactive

We have the dismiss transition working, but we also want to manage the transition via users interactions such as tap, swipe etc.
In our example we will try to manage the transition with Pan gesture.

First we should tell UIKit that this view controller has an interactionController. This is done by overriding the transitionDelegate method in PiecesViewController:

While moving the touch, -pan: method will be called continously, with state UIGestureRecognizerStateChanged . we calculated how much we move our finger on the x-axis. While delta is guaranteed to be [0,1], we are going to update our transition with the delta proportionally.

Finally when the gesture ends, we will check the delta. In our case, to complete the dismiss transition, we should have move our finger at least %70 of the whole transition. If not the transition is not completed then we call -cancelInteractiveTransition method. If we moved the finger enough we will call -finishInteractiveTransition.

Note that the methods -updateInteractiveTransition:,-cancelInteractiveTransition and -finishInteractiveTransition are methods of the protocol <UIViewControllerInteractiveTransitioning>. We already have that protocol because we have UIPercentDrivenInteractiveTransition as superclass, remember ?

Here is the result with interaction:

Summary

We have completed basics of UIViewControllerTransioning API. Transitions possibilities are endless with the new API and you can create and attach transitions to different view controllers with no change. Here is the complete source code for the project. Note that the project contains more than the transitions and implementations we discussed here.