title: UIViewController interactive transition - Presented view disappears when interactive dismiss is canceled
body: <p>I have a demo app where I'm trying to mimic the Mail app "new message" interactive transition - you can drag down to dismiss, but if you don't drag far enough the view pops back up and the transition is cancelled. I was able to duplicate the transition and interactivity in my demo app, but I noticed that when the dismiss transition is cancelled, the presented view controller is animated back up into place, then vanishes. Here's what it looks like:</p>
  
  <p><a href="https://i.stack.imgur.com/xiXJU.gif" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/xiXJU.gif" alt="enter image description here"></a></p>
  
  <p>My best guess is that the transition context's container view is being removed for some reason, since I added the presented view controller's view to it. Here is the presentation and dismiss code inside the <code>UIViewControllerAnimatedTransitioning</code> objects:</p>
  
  <p><strong>Show Transition</strong></p>
  
  <pre><code>func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
      guard let fromVC = transitionContext.viewController(forKey: .from),
          let toVC = transitionContext.viewController(forKey: .to)
          else {
              return
      }
  
      let containerView = transitionContext.containerView
      let finalFrame = transitionContext.finalFrame(for: toVC)
      let duration = transitionDuration(using: transitionContext)
      let topSafeAreaSpace = fromVC.view.safeAreaInsets.top // using fromVC safe area since it's on screen and has correct insets
      let topGap: CGFloat = topSafeAreaSpace + 20
  
      containerView.addSubview(toVC.view)
  
      toVC.view.frame = CGRect(x: 0,
                               y: containerView.frame.height,
                               width: toVC.view.frame.width,
                               height: toVC.view.frame.height - 30)
  
      UIView.animate(withDuration: duration, animations: {
          toVC.view.frame = CGRect(x: finalFrame.minX,
                                   y: finalFrame.minY + topGap,
                                   width: finalFrame.width,
                                   height: finalFrame.height - topGap)
  
          let sideGap: CGFloat = 20
          fromVC.view.frame = CGRect(x: sideGap,
                                     y: topSafeAreaSpace,
                                     width: fromVC.view.frame.width - 2 * sideGap,
                                     height: fromVC.view.frame.height - 2 * topSafeAreaSpace)
          fromVC.view.layer.cornerRadius = 10
          fromVC.view.layoutIfNeeded()
      }) { _ in
          transitionContext.completeTransition(true)
      }
  }
  </code></pre>
  
  <p><strong>Dismiss Transition</strong></p>
  
  <pre><code>func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
      guard let fromVC = transitionContext.viewController(forKey: .from),
          let toVC = transitionContext.viewController(forKey: .to)
          else {
              return
      }
  
      let finalFrame = transitionContext.finalFrame(for: toVC)
      let duration = transitionDuration(using: transitionContext)
  
      UIView.animate(withDuration: duration, animations: {
          toVC.view.frame = finalFrame
          fromVC.view.frame = CGRect(x: fromVC.view.frame.minX,
                                     y: finalFrame.height,
                                     width: fromVC.view.frame.width,
                                     height: fromVC.view.frame.height)
  
          toVC.view.layer.cornerRadius = 0
  
          toVC.view.layoutIfNeeded()
      }) { _ in
          transitionContext.completeTransition(true)
      }
  }
  </code></pre>
  
  <p>And here is the code in the <code>UIPercentDrivenInteractiveTransition</code> object:</p>
  
  <pre><code>func handleGesture(_ gesture: UIPanGestureRecognizer) {
      #warning("need to use superview?")
      let translation = gesture.translation(in: gesture.view)
      var progress = translation.y / 400
      progress = min(1, max(0, progress)) // constraining value between 1 and 0
  
      switch gesture.state {
      case .began:
          interactionInProgress = true
          viewController.dismiss(animated: true, completion: nil)
      case .changed:
          shouldCompleteTransition = progress &gt; 0.5
          update(progress)
      case .cancelled:
          interactionInProgress = false
          cancel()
      case .ended:
          interactionInProgress = false
          if shouldCompleteTransition {
              finish()
          } else {
              cancel()
          }
      default:
          break
      }
  }
  </code></pre>
  
  <p>Any help would be greatly appreciated. It's worth noting I used this Ray Wenderlich tutorial as a reference - 
  <a href="https://www.raywenderlich.com/322-custom-uiviewcontroller-transitions-getting-started" rel="nofollow noreferrer">https://www.raywenderlich.com/322-custom-uiviewcontroller-transitions-getting-started</a>
  However where they used image snapshots to animate the transition I'm using the view controller's views.</p>