Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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:

  • Use right-directional pan gesture: will trigger an interactive transition that will slide VC B back to the right and uncover VC A. The position of VC B is determined interactively by the pan gesture state. The interaction is 'driven' by a UIPercentDrivenInteractiveTransition object.
  • Use a "close" button on VC B navbar. This will trigger the custom transition (slide to right) with no interaction (just animated).

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:

  • UI is stuck on VC B and no gestures or taps work.
  • The app isn't stuck - I can see conosle logs still rolling and netwrok activity is working (no errors in the log)
  • Pausing the app in this stuck state I can see com.apple.main-thread is not stuck.
  • When I hit "Debug View Hierarchy" something weird happens: on sim screen I can still see VC B and all UI is disabled. On view debugger main view - I can see VC A's subviews drawn as if the transition is done. On view debugger left tree view - I can see the view hierarcgy of VC B.

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.

like image 535
Shai Ben-Tovim Avatar asked Feb 18 '26 01:02

Shai Ben-Tovim


1 Answers

Try setting wantsInteractiveStart = false in your UIPercentDrivenInteractiveTransition

like image 187
jose saad Avatar answered Feb 21 '26 15:02

jose saad