jomafer
jomafer

Reputation: 2725

Animate root view controller embedded into a navigation controller presented modally

I need to animate a UIViewController which is the root of a UINavigationController. This navigation controller is presented modally. I know I can do it with the lifecycle methods of the view controller, but I want to use a UIViewControllerAnimatedTransitioning if possible to keep the VC code clean.

I tried the following:

a) Set the UINavigationControllerDelegate to the navigation controller, so I could use this delegate function:

func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationController.Operation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning?

However, it only gets called during transitions within the navigation controller (from VC to VC).

b) Set a UIViewControllerTransitioningDelegate in the root view controller and use this delegate function:

func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning?

But again, it doesn't get called as the one presenting is the navigation controller.

c) Set a UIViewControllerTransitioningDelegate in the navigation controller and use the same delegate function:

func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning?

In this case, it gets called and I can inject the UIViewControllerAnimatedTransitioning object, but I can only think about hacks to animate the root, e.g.

func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
    guard let fromVC = transitionContext.viewController(forKey: .from),
          let toVC = transitionContext.viewController(forKey: .to) as? UINavigationController,
          toVC.children.count == 1,
          let myRootVC = toVC.children.first as? MyViewController
    else {
        transitionContext.completeTransition(false)
        return
    }
    // Animate manually
    myRootVC.view.alpha = 0
    // [...]
}

Do you know any way to capture this nicely?

Upvotes: 1

Views: 209

Answers (1)

Grzegorz Krukowski
Grzegorz Krukowski

Reputation: 19872

I solved the similar problem by making all my ViewControllers being presented as embedded ViewControllers using ViewController Containment.

I do have 1 RootContainerViewController setup when launching the the application - it's simply set as rootViewController without animations and it's just container for all other ViewControllers.

All other ViewControllers are always added as SubViewControllers and SubViews to that container.

That allows you not only to use UIViewControllerAnimatedTransitioning for every single transition in your application, but also it allows you to easily make "reset" of the app, by just removing RootContainerViewController and replacing it with fresh one. Another advantage is that you can add as many ViewControllers as you want - compared to native presentViewController that allows you to have only 1 ViewController presented at a time.

Any other approaches I tried ended up badly - UIViewControllerAnimatedTransitioning is impossible to use directly with rootViewController changes - you will have to combine it with UIView.animate to support it.

Upvotes: 1

Related Questions