iOS: Creating a Custom UIViewController Transition Animation — a Step-By-Step Guide

Photo by Xavier Wendling on Unsplash

First of all — what is this ‘Transition animation’, you might ask? Well, no rocket science here — transition animations provide visual feedback about changes to your app’s interface. UIKit provides a set of standard transition styles to use when presenting view controllers, and you can supplement the standard transitions with custom transitions of your own.You may have seen a lot of transition animations that look really nice and neat but you might not have any idea of how to create one. Some of them look really complicated and some really simple, but in the end you just don’t know where to start.

That’s why I am going to introduce you to custom transitions. In this step-by-step guide, we are going to create a regular custom transition animations and, in doing so, wash away any fears you might have had about them. So let’s get started! I

Starter project

have already prepared a demo project that you can clone here:
 This tutorial, as well as the project, is going to be written in Swift 2.2.
 In case you don’t want to use the project that I have provided, you can also create your own. Just make sure to distinguish two UIViewControllers by including some kind of background image or colour so that the transitions are nicely visible.

What is it going to look like?

We are going to start with a demo project and gradually add code to it so that, in the end, we get a transition animation like this one:

Creating a base animator class

First things first: we are going to create an ‘Animator’ object, that is going to be our delegate for animations, so that we can make it reusable for all parts of our apps.

Create a new Swift file, name it ‘TransitionAnimator’ and in the same file create a class with the same name, which is a subclass of NSObject.

import UIKit final class TransitionAnimator: NSObject { }

Now we are ready to conform to the UIViewControllerAnimatedTransitioning protocol in order to trigger custom transition animations. When conforming to this protocol we need to implement 2 methods:

  • public func transitionDuration(transitionContext:
    UIViewControllerContextTransitioning?) -> NSTimeInterval
  • public func animateTransition(transitionContext: UIViewControllerContextTransitioning)

The first one is pretty self-explanatory — you return the total duration of our custom animation. The second one should contain the actual animation logic.

So let’s extend our Animator object to conform to this protocol as well as to implement two methods and return our animation duration as a private let kAnimationDuration constant with value of 0.6.

Alright, we are almost ready to start with the animation logic, except that we lack one detail — we have to differentiate between presenting and dismissing animations. After all, we want to have different animations for when we present a viewController and when we are dismissing one, right?
 To achieve that we can create a little code>enum that is going to nicely encapsulate that information and then make it a private let transitionMode constant which is initialized through our custom init method:

Here we have marked our enum with an @objc attribute; that is in case we want to use this enum from an objective-c file. Our initializer takes one argument, StackModalTransitionMode, that specifies which type of animation we are willing to trigger - presentation or dismissal.

Creating the necessary animation constants

Now the very last thing we need (it really is the last one before we're starting, I promise) is to create some constant properties that we are going to be using for our animation logic. I don't like having a lot of "magic numbers" dangling around in my code, so I always extract them into dedicated constants with some meaningful names.

Don’t go into too much detail as to why the constants are named that way or why I have that many of them — everything will start to make sense as soon as we are using them in our animation logic.

Filling animateTransition with some logic

When an animation is being triggered, you receive a transitionContext argument which contains information about the viewController that is about to be presented as well as the viewController that presents it. Now, to get the animation effect we want, we have to manipulate views or viewControllers and for that we need transitionContext.

View to squeeze:

Let's create a private extension for our class where we will create a helper method that is going to try to extract a view that we need to squeeze for our animation:

First, depending on the transitionMode we select the proper key for our viewController that we are going to squeeze and save that string into the key constant.
 Next, we use guard for extracting the view we are going to squeeze. We are using guard here as the method viewControllerForKey returns an optional UIView? value. We return nil so that a little bit later we would be able to properly back-out from the animation transition in our code.
 After that, we use one more guard for the containerView that we have to use for all our UIViews we want to manipulate in our animation transition - this is an object to add them to. Then we create a screenshot of the view we are going to squeeze.
 Now we create a backgroundView that is going to be visible underneath our view we are about to squeeze. I have moved code for this view into a separate method so that the code is more easily readable.
 As a next step, first we are adding our backgroundView to the containerView and then snapshotToSqueeze on top of that, so that when we are going to squeeze it, we will see a backgroundView beneath it.
 Finally we return snapshotToSqueeze to the caller.
 Now let's call this method from our animateTransition method:

I guess you’ve noticed that we are using guard as well, so in the else clause we are calling the transitionContext.completeTransition(false) method, which in that case means that our animation went wrong and we have completed our custom transition. This is very important, because your view is going to be unresponsive otherwise if you don't do this. The reason for this is simple: if something go wrong, we'd better play it safe and think about the user experience.

View to slide

Now it's time to get the view we are going to present or dismiss. To do so, we have to create another helper method named viewToSlide in our private extension next to our previous one:

Here we are doing pretty much the same thing as in our viewToSqueeze method, but this time we don't need a backgroundView as we are going to slide it over another view. That is why we simply add it to our containerView and return viewToSlide back to our animateTransition method. It will now look like this when we add a call for this method here:

Once again, don’t forget to call transitionContext.completeTransition(false) in case something goes wrong - we don't want to block views!

Preparing for animation

We are ready to use the views we have prepared and now we need to position them properly before rendering the animations. This part is not hard - depending on the transitionMode, we are either moving our view we are about to present all the way down, so that we can pull it back up with the animation, or we are squeezing the view that is underneath our presented one so that we can un-squeeze it back with the animation.

Then, we add a call to this method back to animateTransition:


Alright, we are almost there, we are ready to actually write animation code, yes! So let’s create one method where we decide which animation method to call — the one for presenting or the one for dismissing viewCotroller:

We need to add this call to our animateTransition method, which is going to be the last one there:

At this point, you should now have two warnings for the unimplemented methods startAnimationForPresentingMode and startAnimationForDismissingMode. Well, let's begin with the presenting one:

Here we are simply using two UIView animation methods - one for our snapshot a.k.a view we are going to squeeze and another one for the view that is going to slide up. And here you can finally see the constants we have created at the beginning of this tutorial in use. 
 Let's add the second animation method as well, that we are going to use once we are dismissing our presented viewController.

Connecting TransitionAnimator to UIViewController

Now that we have completed all the animation related stuff, we have one last thing to do before we will be able to actually see the result on our devices — we have to explicitly state that we want to use a custom transition animation and that our TransitionAnimator is going to do all the animation work. In order to do so, you have to force the viewController that is involved in the animation to conform to the UIViewControllerTransitioningDelegate protocol. Once conforming to this protocol, it is necessary to implement two pretty much self explanatory methods: animationControllerForPresentedController and animationControllerForDismissedController. In both cases we are going to use our TransitionAnimator as we have configured it to handle both presenting and dismissing animations through a parameter that we pass along in the init method. Also, as we are specifying our TransitionAnimator as primary for both animations through delegate methods. 
 Now, go to the first viewController that presents the second one. In case you are using the demo project I have provided, then this class is called FirstViewController. Open it and add conformance to protocol as an extension to our class along with two methods.

Now in order for this function to be actually called we have to do one last part — prior to the presentViewController method call we have to specify that we want to use the custom transition animation. For that case, these two methods from the UIViewControllerTransitioningDelegate protocol are going to be called. 
 Navigate to the didTapPresentButton method in the FirstViewController class and add these two lines right after the guard line:


So as you can see from all the steps above, it is not hard to do this at all. However, creating custom transition animations definitely takes time, especially when you start playing around with animation parameters. That’s all from me now — enjoy!

written by: Yarik Arsenkin