Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 55

Custom View Controller Transitions

Recommended resources

Hello! Welcome to the final lesson in our module on "Interactive Animations and Transitions."

In our last lesson, we dove into CAShapeLayer and CABasicAnimation, learning how to create sophisticated line-drawing effects by animating the strokeEnd property. This gave you fine-grained control over animating vector shapes.

Today, we'll use our animation skills in a broader context. Our goal is to implement a custom view controller transition using the UIViewControllerAnimatedTransitioning protocol. This is a hallmark of a polished, professional iOS app, allowing you to move beyond the standard "slide up" or "push" transitions to create unique, branded experiences that guide and delight the user.

1. The Anatomy of a Custom Transition

Before we write any code, it's crucial to understand the main components that UIKit uses to manage a custom view controller transition. When you present one view controller from another, a negotiation happens behind the scenes.

Here are the key players:

  • The Transitioning Delegate (UIViewControllerTransitioningDelegate): An object you provide that acts as a factory. Its job is to supply the system with the custom "animator" object that will perform the transition.
  • The Animation Controller (UIViewControllerAnimatedTransitioning): This is the object that does the actual work. You implement its methods to define the duration of the transition and, most importantly, the animations themselves.
  • The Transitioning Context (UIViewControllerContextTransitioning): The system creates this object and passes it to your animation controller. It's a treasure trove of information, giving you access to the "from" and "to" view controllers, their views, and the all-important container view.
  • The Container View: This is a temporary view, provided by the context, that serves as the stage for your animation. All transition animations take place within this view's coordinate space.

The following diagram illustrates how these components interact.

UIKit Custom View Controller Transition Components Diagram
This diagram shows the relationship between the View Controller, the Transitioning Delegate (which vends an Animation Controller), the Transitioning Context (provided by UIKit), and UIKit itself.

To solidify the concept of the container view, take a moment to read this brief explanation.

Custom UIViewController transitions in Swift | by Tung Fam - Medium

The article "Custom UIViewController transitions in Swift" provides a great, simple explanation of the container view's role.

Please read the short section titled "Some theory 🤓". Focus on how it describes the Transition Container View as a temporary view that exists for the duration of the animation.

With this theoretical foundation, we're ready to build a custom transition from scratch.


2. Implementing a Custom Presentation and Dismissal

We'll now walk through the process of creating a custom transition that scales a view controller into view with a spring effect and dismisses it with a more complex keyframe animation. The following video tutorial is an excellent, practical guide that covers all the necessary steps.

Custom View Controller Transitions

We'll be following the "Custom View Controller Transitions" video from Code Pro. It provides a clear, step-by-step implementation of both a presentation and dismissal animation.

First, watch from the beginning up to 04:27. This part introduces the goal and shows how to create the AnimationController class, conform it to NSObject and UIViewControllerAnimatedTransitioning, and stub out the required methods (transitionDuration and animateTransition).

The Animator Object

As you saw in the video, the first step is creating our animator class. This class, which the video calls AnimationController, will conform to UIViewControllerAnimatedTransitioning. It has two essential methods:

  1. transitionDuration(using:): Here, you return the total duration of your animation in seconds.
  2. animateTransition(using:): This is where the magic happens. You receive the transitionContext and use it to orchestrate the animation.

Implementing the "Present" Animation

Now, let's focus on the presentation animation. The process follows a clear pattern:

  1. Get a reference to the views and view controllers from the transitionContext. You'll need the containerView, the to view controller (the one being presented), and sometimes the from view controller.
  2. Add the to view controller's view to the containerView. This is a critical step; without it, the view you want to show won't be in the view hierarchy.
  3. Set the initial state of the to view. For a scaling animation, this means setting its transform to be scaled down to zero.
  4. Animate to the final state inside a UIView.animate block. Here, you reset the transform to the identity (normal size).
  5. Call completeTransition() in the animation's completion handler to signal to UIKit that the transition is finished.

Let's watch this process in action.

Custom View Controller Transitions

Let's continue with the video.

Watch from 04:27 to 20:05. This long segment covers two key parts: Implementing presentAnimation (04:27 - 17:16): Pay close attention to how the transitionContext is used to get the views, how the toView is added to the containerView, and how the transform property is animated. Notice the call to transitionContext.completeTransition(true) in the completion block. Wiring up the transitioningDelegate (17:16 - 20:05): See how the presented view controller conforms to UIViewControllerTransitioningDelegate and returns an instance of our AnimationController from the animationController(forPresented:...) method. The transitioningDelegate property must also be set.

The Importance of clipsToBounds

The video makes an excellent point about a subtle visual artifact that can occur during scaling animations.

Custom View Controller Transitions

Now, let's see a quick but important demonstration.

Watch the segment from 20:05 to 22:09. This explains why setting clipsToBounds = true on the animating view is important to prevent its content from 'spilling out' during the scaling effect.

This is a great example of the attention to detail required for professional-level animations.

Implementing the "Dismiss" Animation with Keyframes

Dismissal follows a similar pattern, but with the roles reversed: the view being dismissed is the from view. For our dismissal animation, we'll use UIView.animateKeyframes to create a multi-stage animation: first scaling down, then moving off-screen while fading out.

This demonstrates how you can compose more complex sequences than a simple UIView.animate block allows.

Custom View Controller Transitions

Let's implement the dismissal animation.

Finally, watch from 22:09 to the end (33:59). This section covers: Implementing dismissAnimation (22:09 - 30:54): Focus on the use of UIView.animateKeyframes to chain multiple animations (scaling, translation, and alpha). Also, note how the animationController(forDismissed:...) delegate method is implemented. The Black Screen Problem (30:54 - 33:59): This is a crucial professional tip. When dismissing, you must add the toView (the view controller you are returning to) to the containerView behind the fromView to prevent a black screen during the animation. Pay close attention to this explanation.

The logic for handling the dismissal transition's container view is a common source of bugs for developers new to this topic. Ensuring the toView is present provides visual continuity and prevents the jarring appearance of a black void.


3. Summary of the Transition Process

You've just seen a complete, end-to-end implementation of a custom view controller transition. Let's recap the essential steps, which are also summarized well in the LINK resource.

  1. Set the Delegate: Set the transitioningDelegate property on the view controller you are about to present.
  2. Conform to the Delegate Protocol: The delegate object must conform to UIViewControllerTransitioningDelegate.
  3. Provide Animators: Implement animationController(forPresented:...) and animationController(forDismissed:...) to return your custom animator object.
  4. Create the Animator: Your animator class must conform to UIViewControllerAnimatedTransitioning.
  5. Implement transitionDuration(using:): Return the animation duration.
  6. Implement animateTransition(using:):
    • Get the containerView, fromView, and toView from the context.
    • Add the necessary views to the containerView.
    • Set the initial state of the views.
    • Use UIView.animate or UIView.animateKeyframes to perform the animation.
    • In the animation's completion block, remove any temporary views and call transitionContext.completeTransition(!wasCancelled).

Conclusion

Mastering custom view controller transitions is a significant step toward building truly unique and high-quality apps. You now have the power to define exactly how users navigate between different parts of your application, creating an experience that is both functional and memorable.

Key Takeaways:

  • Custom transitions are managed by a transitioningDelegate that vends an animationController.
  • The animation controller conforms to UIViewControllerAnimatedTransitioning and performs the animation within the containerView provided by the transitionContext.
  • The core steps are: add the toView, set initial properties, animate to final properties, and call completeTransition().
  • For dismissals, remember to add the toView to the container to avoid showing a black background.

As a final thought on professionalism, the approach we took today is great for a specific transition. In larger apps, you might create a dedicated manager class (like the ModalTransitionManager described in the article "Mastering view controller transitions, part 1: Make them reusable") to handle vending animators. This makes your transition logic more reusable and decoupled from your view controllers, a key principle of clean architecture.

Preview of the Next Lesson:

We have now learned to build complex, interactive, and custom animations. But with great power comes great responsibility! A beautiful animation that stutters or drops frames can be worse than no animation at all.

In our next module, we will shift our focus to performance. In the first lesson, you will learn how to profile rendering performance and identify dropped frames using the Core Animation instrument in Xcode's Instruments tool. This will give you the skills to ensure your animations are as smooth and efficient as they are beautiful.