UINavigationController Pop - issue w/ custom transition animations

It's been a really long time since I've tried this, so I'm not sure if something has changed or if I've stumbled onto a bug...

I'm trying to implement a custom Transition Animation for a UINavigationController. While documentation around this is pretty sparse this days, I was able to take the old sample from the View Controller Programming Guide:, rewriting it in Swift:

func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
   let containerView = transitionContext.containerView
   guard let fromVC = transitionContext.viewController(forKey: .from),
      let toVC = transitionContext.viewController(forKey: .to),
      let toView = transitionContext.view(forKey: .to),
      let fromView = transitionContext.view(forKey: .from) else {
         transitionContext.completeTransition(false)
         return
      }

      let containerFrame = containerView.frame
      var toViewStartFrame = transitionContext.initialFrame(for: toVC)
      let toViewFinalFrame = transitionContext.finalFrame(for: toVC)
      var fromViewFinalFrame = transitionContext.finalFrame(for: fromVC)
      let fromViewStartFrame = transitionContext.initialFrame(for: fromVC)

      if operation.isPresenting {
         toViewStartFrame.origin.x = containerFrame.size.width
         toViewStartFrame.origin.y = containerFrame.size.height
      } else {
         fromViewFinalFrame = CGRect(x: containerFrame.size.width,
                                        y: containerFrame.size.height,
                                        width: toView.frame.size.width,
                                        height: toView.frame.size.height)
         // missing from Apple's sample code
         toViewStartFrame = toViewFinalFrame
      }

      containerView.addSubview(toView)
      toView.frame = toViewStartFrame

      // Add the from view to the container view on dismissal, this is missing from Apple's sample code
      if !operation.isPresenting {
         containerView.addSubview(fromView)
          fromView.frame = fromViewStartFrame
      }

      UIView.animate(withDuration: transitionDuration(using: transitionContext)) {
         if self.operation.isPresenting {
            toView.frame = toViewFinalFrame
         } else {
            fromView.frame = fromViewFinalFrame
         }
      } completion: { completed in
         let success = !transitionContext.transitionWasCancelled

         if (self.operation.isPresenting && !success) || (!self.operation.isPresenting && success) {
             toView.removeFromSuperview()
          }
          
          // missing from Apple's sample code
          if !self.operation.isPresenting {
             fromView.removeFromSuperview()
          }

          transitionContext.completeTransition(success)
     }
 }

I added a couple of things to support dismissals and pops.

In order to use it with in my app, I set the navigation controller's delegate and returned the type conforming to UIViewControllerAnimatedTransitioning, containing the above code, from navigationController(_ :, animationControllerFor operation:, from fromVC:, to toVC). I've confirmed that that all behaves as you expect.

If I use this animation controller for a modal presentation or dismissal, it works fine. For a Navigation push, again, behaves as expected. But when I use it for a navigation pop I run into a problem. The animation is performed, but once it completes, the Navigation Controller's view appears to be completely empty. In a sample app, the screen goes black. In the View Debugger, I see that the UIViewControllerWrapperView has no subviews. Another curious thing I found is that my View Controller never gets a viewWillDisappear message sent.

Is there an additional setup step that I missed in order to get this working properly?

  • I do believe this to be an SDK bug, or at the very least a documentation bug, so I filed FB13724367

Add a Comment