How to make Custom View Controller Transitions In iOS from Scratch.

Josh Arnold
6 min readDec 20, 2020
An example of some custom View Controller transitions I made. The links to these are at the end of the article!

Introduction

What are UIViewController Transitions?

UIViewController transitions simply refer to how one view controller can transition to another view controller.

Why Learn How to Make Custom Transitions?

Custom transitions allow you to implement interesting UX in your app, for instance, a sliding menu coupled with an elegant animation.

You can make your app feel more seamless and integrated too.

For example, in the GIF above, the image you tap actually expands and jumps out of the view controller, filling the screen to become a new view controller (Instagram & the app store implement similar transitions).

Learning custom transitions from scratch means you have TOTAL control over how your view controllers transition.

Using a third-party library like Hero means you can make quite a few cool transitions, but if you really want to push custom transitions to their limits, it's totally worth learning how to implement them from the ground up.

Ultimately, custom transitions can really add a professional touch to your application. The great news is if you’re already familiar and comfortable with Swift, learning custom transitions is actually pretty straight forward.

Tutorial: Let’s Make a Custom Transition

What we’re going to be building.

Tutorial Break Down

In this tutorial, we’re going to make what you see in the above GIF. Here’s what we’re going to talk about in order.

  1. How do custom transitions work?
  2. Building the PresentAnimator
  3. Building the DismissAnimator
  4. Putting everything together in the UIViewControllerTransitioningDelegate conforming class we make.

How do custom transitions work?

A high-level overview of how custom transitions work.

If you’re already familiar with swift, you should understand how one view controller presents another view controller.

In this diagram, VC1 is presenting VC2.

To do this, typically, you might have some code that looks like this:

// Inside the first view controller (VC1)func onTap() {
let vc = SomeViewController()
self.present(vc, animated: true, completion: nil)
}

The only difference between a normal transition and a custom transition is we have to set a view controller’s transitioningDelegate. That is literally it.

So, to set a view controller’s transitioningDelegate, the previous code would now become something like:

// Inside a view controllerfunc onTap() {
let vc = SomeViewController()
vc.transitioningDelegate = self.myCustomTransitioningDelegate
self.present(vc, animated: true, completion: nil)
}

The question now becomes: how do we create a custom transitioningDelegate? Or more importantly, how do transition delegates work?

Transitioning Delegates Explained

When we refer to transitioning delegates, we are really referring to UIViewControllerTransitioningDelegate protocol.

At a high-level, the primary job of a class that implements this protocol is to manage and return what I call Animators .

There are two animators:

  • A present animator
  • A dismiss animator

“Animators”

The job of an animator is to literally define how the transition animation occurs.

An animator has access to the start view controller and the finish view controller, and from there, it can simply define an animation using UIView.animate for example.

Ultimately, an animator can just be thought of as a function defining an animation between two views. That’s pretty much it.

Summary

At a high-level, all we have to do is create a class that implements the UIViewControllerTransitioningDelegate protocol.

The class will need to have two functions that return both a present animator and a dismiss animator, which are essentially objects/functions that define an animation between two views.

Now, let’s build a custom transition.

Programming a Custom Transition

Firstly, you’re going to need to create a new Xcode Project. To keep this article concise, I will omit the whole process of setting up the project and jump straight to writing code.

Step 1: Creating the PresentAnimator

Our first step is to create the PresentAnimator class.

The job of the present animator is to wrap a function that animates the transitions between two views.

To get started, create a new swfit file in your project called PresentAnimator.swift.

Let’s take a look at the final code for our PresentAnimator :

Let’s break down what is happening.

The following lines of code give us access to the view’s of the view controllers we are transitioning between:

guard let start = transitionContext.view(forKey: .from) else {
return
}
guard let finish = transitionContext.view(forKey: .to) else {
return
}

From there, we simply set different properties of the views, such as their position, size, corner radius, etc—we are setting up the the initial position of the views before we start animating them.

Most importantly, we call transitionContext.containerView.addSubview(finish) to add our finish view to our transition context. Without this line of code, our final view controller would literally not be presented, and nothing would work.

Next, we make a call to UIView.animateKeyframes , which is where we literally animate the transition. All I’m doing is animating size, position, etc etc. Nothing fancy here.

Lastly, after the animation has finished, we call transitionContext.completeTransition(success) , which is required to make sure the transition finishes successfully & smoothly.

That’s literally the whole process for defining the PresentAnimator . It is a few lines of code, but you can make the animation as simply or complex as possible.

Next, lets work on the dismiss animator.

Step 2: Creating the DismissAnimator

The dismiss animator is exactly like the PresentAnimator , except its job is to animate the transition between the presented view controller and the original view controller: a.k.a, the dismiss animation.

Below is the class we need to implement the dismiss animator.

Everything is almost identical to the PresentAnimator , except, we are animating the currently presented view controller “off screen” instead of “on screen” like in the PresentAnimator .

Notice after adding the finish view to the transitionContext.containerView , we also call sendSubviewToBack . The reason we do this is because our finish view is the original view controller that presented the current view controller. We want our original view to begin “underneath” the current viewto give the appearance of a dismiss animation rather than a presentation animation.

In summary: we againget access to the start and finish views, we update their properties, then animate them. This should be nothing new to you if you’ve programmed a decent amount in Swift before.

Step 3: CustomTransition.swift

In our third step, it’s as simple as creating one last NSObject subclass.

We make sure we confirm to the UIViewControllerTransitioningDelegate protocol, and add the following two functions:

Notice all we need to do is return our PresentAnimator() and DismissAnimator() for each respective function? It’s that easy.

Step 4: Using Our Custom Transition

Now, how do we use our custom transition?

Let’s imagine we have a view controller class like so:

If we want to present a view controller using our custom transition, all we need to do is the call handlTransition() function above (as an example).

We essentially set the vc.transitioningDelegate = self.transitionDelegate , and also vc.modalPresentationStyle = .fullscreen , and that's literally all we need to get our custom transition working.

If you run your app and try to transition between views, you should now see a fantastic custom animation. Great work!

Conclusion

Don’t be scared of custom transitions. Don’t worry about the large amount of code in the PresentAnimator or DismissAnimator either (their function is actually really straight forward—just animate between two views—that’s it).

Ultimatelly, understanding the above design patterns means the complexity of how to make a custom transition is reduced instead to how to make an animation between two UIViews?

Below are two links to two demo projects where I implemented more complex transitions, including an interactive transition.

Feel free to check them out, learn from them, and star them if you found the example project useful.

Lastly, feel free to shoot me an email at dev@josharnold.me if you have any questions about the implementation.

Thanks for reading!

--

--