xcode10ios12custom-transition

Xcode 10: Custom Animated Transitions Stuck


My app has two View Controllers (VC A & B) and a custom transition between them.

When using a left-directional pan gesture on VC A, an interactive animated transition modally presents VC B over A sliding in from the right (right-to-left). To dismiss VC B, user can:

Problem is that testing on Xcode 10 Seed (build 10A254a) + iOS 12 Simulator (X or XR or XS) I can easily get to a state where the custom transition never completes and the UI is left hanging in a weird state:

This problem never appeared on any previous version of Xcode and/or iOS pre Xcode 10/iOS12.

This is my animateTransition method in my custom UIViewControllerAnimatedTransitioning

func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {

    guard let fromVC = transitionContext.viewController(forKey: .from), let toVC = transitionContext.viewController(forKey: .to) else {
        transitionContext.completeTransition(false)
        return
    }

    let containterView = transitionContext.containerView
    containterView.insertSubview(toVC.view, belowSubview: fromVC.view)

    let bounds = fromVC.view.bounds
    var xOffsetMultiplier : CGFloat = 0.0
    var yOffsetMultiplier : CGFloat = 0.0

    switch direction {
    case .up:
        yOffsetMultiplier  = -1.0
    case .right:
        xOffsetMultiplier  = 1.0
    case .left:
        xOffsetMultiplier  = -1.0
    case .down:
        yOffsetMultiplier  = 1.0
    }

    print(xOffsetMultiplier,bounds.size.width,bounds.size.height )
    UIView.animate(withDuration: duration, animations: {
        print("animating...")
        //fromVC.navigationController?.navigationBar.alpha = 0.0
        fromVC.view.frame = fromVC.view.frame.offsetBy(dx: xOffsetMultiplier * bounds.size.width, dy: yOffsetMultiplier * bounds.size.height)
    }, completion: { finished in
        print("completed animation")
        transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
        //fromVC.navigationController?.navigationBar.alpha = 1.0
    })

}

The prints are there just for debug.

This is the sequence that easily recreates the problem:

  1. Use pan gesture to start interactive transition from B back to A but never complete it - this will call cancel() on the UIPercentDrivenInteractiveTransition object + I can verify the animation is completed.
  2. Tap the 'close' button to invoke the non-interactive transition to dismiss B. B never dismisses and the custom animation never completes!

On a device I couldn't recreate this issue at all (yet) - and all transitions working as expected.


Solution

  • So found the source of why transitions where stuck between VC B and A but still don't really understand what's different between Xcode9/iOS11 and Xcode10/iOS12 that produced the different behaviour.

    To keep it short:

    Making sure the dismiss transition is either interactive or not based on the user interaction, especially after canceling an interactive one, fixed the problem.

    What I don't understand is why isn't this consistent behaviour between Xcode/iOS versions. This problem never ever happened to me before on any device or simulator. There's something different in the way custom animations/transitions are handled - nothing in the Apple docs that could explain this - perhaps in the internal implementation of the transition context.

    From a naive "eye-test" it seems that transition animations on the Xcode10 simulator are slower in reaction time and less smooth than before but still doesn't fully explain it.