How do you build a custom view controller transition, including an interactive one?

7.3.4 · L5 · Interview

Predict first

Does this minimal animator compile?

import UIKit

final class FadeAnimator: NSObject, UIViewControllerAnimatedTransitioning {
    func transitionDuration(using ctx: UIViewControllerContextTransitioning?) -> TimeInterval { 0.3 }
    func animateTransition(using ctx: UIViewControllerContextTransitioning) {
        guard let to = ctx.view(forKey: .to) else { return ctx.completeTransition(false) }
        ctx.containerView.addSubview(to)
        to.alpha = 0
        UIView.animate(withDuration: 0.3, animations: { to.alpha = 1 }) { _ in
            ctx.completeTransition(!ctx.transitionWasCancelled)
        }
    }
}
  • Yes
  • No

Mental model. The animator is a choreographer given the dancers (the two views) and the stage (the container). They must always finish by announcing “the scene is over”.

A transition has three roles. A transitioning delegate (UIViewControllerTransitioningDelegate for modals, UINavigationControllerDelegate for push and pop) is asked for the animator. An animator (UIViewControllerAnimatedTransitioning) provides transitionDuration and animateTransition(using:). Inside it you receive a UIViewControllerContextTransitioning object: take the containerView, fetch the viewController(forKey:) for from and to, add the incoming view to the container, run your animation, and always call transitionContext.completeTransition(!transitionContext.transitionWasCancelled), or the UI freezes.

For interactive transitions, also return a UIViewControllerInteractiveTransitioning, typically a UIPercentDrivenInteractiveTransition, and drive it from a gesture: update(_:) with the progress, then finish() or cancel(). To change the chrome around a modal (a dimming view, a custom frame) subclass UIPresentationController. Newer SDKs add built-in zoom transitions (preferredTransition = .zoom(...)) that replace most hand-written ones. Pitfalls: not completing the transition, leaving the “from” view in the container, animating with Auto Layout instead of explicit frames or transforms, and ignoring cancellation.

Two methods are required: the duration and the animation. It ends by completing the transition, which is what releases the UI.

Common traps

  • Not calling completeTransition. The screen hangs, with the old view stuck in the container.
  • Ignoring a cancelled interactive transition. Pass the right success flag, or the hierarchy ends up wrong.

They might follow up with

  • When do you need a UIPresentationController? When the presented view is not full screen and needs its own surrounding views, such as a dimmed background.
  • How do you drive one with a pan gesture? Create the interactive transition when the gesture begins and update it with the translation.

Related questions

  • 7.3.3 How does modal presentation work, and how do the presentation styles and sheets differ?
  • 7.3.1 What is the view controller lifecycle, and what belongs in each method?

← How does modal presentation work, and how do the presentation styles…