Custom modally-presented UIViewController from another UIViewController

As a fast follow-up from the previous Custom UINavigationController Animated Transitions post, let’s say you have a UIViewController up and you want to transition to another UIViewController, but you are not using segues. The simple solution: present.

Create an animator like a fading animation controller

class FadeAnimator: NSObject, UIViewControllerAnimatedTransitioning {
    var duration: TimeInterval = 2.0

    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        return duration
    }

    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        let containerView = transitionContext.containerView
        let toView = transitionContext.view(forKey: .to)!

        containerView.addSubview(toView)
        toView.alpha = 0

        UIView.animate(withDuration: duration,
                       animations: { toView.alpha = 1.0 },
                       completion: { _ in transitionContext.completeTransition(true) } )
    }
}

Extend your UIViewController as a transition delegate

Easy peasy: just extend the existing class, in this case to do a fade animation.

extension DetailViewController: UIViewControllerTransitioningDelegate {
    func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return FadeAnimator()
    }
}

Finally, instantiate and present!

Ready to show the next UIViewController? Let’s say you want to show a SubdetailViewController:

        let subdetailVC = SubdetailViewController()
        subdetailVC.view.frame = view.frame
        subdetailVC.transitioningDelegate = self
        present(subdetailVC, animated: true, completion: { })

The two bits of magic here are setting your “from” view controller as the transitioning delegate of the new view controller. Then you present with animations. The new controller will be presented modally over the old one.

One more thing: dismissing the new view controller

And when you’re ready to get rid of the new view controller, you can do it from inside it:

    dismiss(animated: true, completion: { })

That’s it!