Coming soon - Get a detailed view of why an account is flagged as spam!
view details
23
How do I create a custom view controller transition that is interruptable/cancellable?
Post Body

I have a small custom view controller (it's a menu) I'm presenting, and I want you to be able to halt the presentation half way through if you change your mind.

Per this WWDC 2016 video, doing so should be pretty easy and just require the implementation of interruptibleAnimator(using transitionContext: UIViewControllerContextTransitioning) -> UIViewImplicitlyAnimating, and return, say a UIViewPropertyAnimator.

However I'm not finding this to be the case. If I return my property animator there, and programmatically call my view controller to be dismissed half way through the transition, it waits until the animation is fully concluded for the dismiss animation to occur. It won't interrupt.

*Note: this is with a "normal" animation controller, not an interactive one. Per the video this should be fine, it's not inherently an interactive transition"

I set up my corresponding controllers as shown:

```swift func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? { return MyMenuAnimationController(type: .presentation) }

func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { return MyMenuAnimationController(type: .dismissal) }

func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? { return MyMenuPresentationController(presentedViewController: presented, presenting: presenting) } ```

And the specified animation controller (as mentioned there's no interaction controller as there's no gesture recognizer or anything attached to this transition, it only fires on tap):

```swift class MyMenuAnimationController: NSObject, UIViewControllerAnimatedTransitioning { enum AnimationControllerType { case presentation, dismissal }

let type: AnimationControllerType

var animatorForCurrentSession: UIViewPropertyAnimator?

init(type: AnimationControllerType) {
    self.type = type
}

func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
    return 5.0 // Artificially long for testing
}

func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
    if type == .presentation, let myMenuMenu: MyMenuMenu = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to) as? MyMenuMenu {
        transitionContext.containerView.addSubview(myMenuMenu.view)
    }

    interruptibleAnimator(using: transitionContext).startAnimation()
}

func interruptibleAnimator(using transitionContext: UIViewControllerContextTransitioning) -> UIViewImplicitlyAnimating {
    if let animatorForCurrentSession = animatorForCurrentSession {
        return animatorForCurrentSession
    }

    let propertyAnimator = UIViewPropertyAnimator(duration: transitionDuration(using: transitionContext), dampingRatio: 0.75)
    propertyAnimator.isInterruptible = true
    propertyAnimator.isUserInteractionEnabled = true

    let isPresenting = type == .presentation

    guard let myMenuMenu: MyMenuMenu = {
        return (isPresenting ? transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to) : transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from)) as? MyMenuMenu
    }() else {
        preconditionFailure("Menu should be accessible")
    }

    myMenuMenu.view.frame = transitionContext.finalFrame(for: myMenuMenu)

    let initialAlpha: CGFloat = isPresenting ? 0.0 : 1.0
    let finalAlpha: CGFloat = isPresenting ? 1.0 : 0.0

    myMenuMenu.view.alpha = initialAlpha

    propertyAnimator.addAnimations {
        myMenuMenu.view.alpha = finalAlpha
    }

    propertyAnimator.addCompletion { (position) in
        guard position == .end else { return }
        transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
        self.animatorForCurrentSession = nil
    }

    self.animatorForCurrentSession = propertyAnimator
    return propertyAnimator
}

private func calculateTranslationRequired(forMyMenuMenuFrame myMenuMenuFrame: CGRect, toDesiredPoint desiredPoint: CGPoint) -> CGVector {
    let centerPointOfMenuView = CGPoint(x: myMenuMenuFrame.origin.x   (myMenuMenuFrame.width / 2.0), y: myMenuMenuFrame.origin.y   (myMenuMenuFrame.height / 2.0))
    let translationRequired = CGVector(dx: desiredPoint.x - centerPointOfMenuView.x, dy: desiredPoint.y - centerPointOfMenuView.y)
    return translationRequired
}

} ```

And a simple presentation controller:

swift class MyMenuPresentationController: UIPresentationController { override var frameOfPresentedViewInContainerView: CGRect { return CGRect(x: 100, y: 100, width: 100, height: 100) } }

But as I said, if I present it simply and then 1 second into the 5 second animation I dismiss it (signifying someone tapping outside the view controller to dismiss it mid transition), nothing occurs until the animation is done. It's as if it's queued up, but won't execute until the presentation is completed.

```swift let myMenuMenu = MyMenuMenu() present(myMenuMenu, animated: true, completion: nil)

DispatchQueue.main.asyncAfter(deadline: .now() .seconds(1)) { myMenuMenu.dismiss(animated: true, completion: nil) } ```

Am I misunderstanding a core part of the custom view controller transition flow? Do I have to make it an interactive transition? Something else? In the event that an interactive transition is required, where do I update the interaction controller in my code with progress? With a gesture it's obvious, but this is just a "fade 0 alpha to 1 alpha" animation.

Author
Account Strength
100%
Account Age
13 years
Verified Email
Yes
Verified Flair
No
Total Karma
1,715,057
Link Karma
529,042
Comment Karma
607,499
Profile updated: 5 days ago
Posts updated: 3 months ago
Objective-C / Swift

Subreddit

Post Details

We try to extract some basic information from the post title. This is not always successful or accurate, please use your best judgement and compare these values to the post title and body for confirmation.
Posted
3 years ago