UIPercentDrivenInteractiveTransition Cancelling Issue
Asked Answered
P

1

12

What I Have

I am using UIViewControllerAnimatedTransitioning protocol with an attached UIViewPropertyAnimator to pan down to dismiss a View Controller

extension SecondViewController : UIViewControllerAnimatedTransitioning {
    func interruptibleAnimator(using ctx: UIViewControllerContextTransitioning) -> UIViewImplicitlyAnimating {

        if self.animator != nil {
            return self.animator!
        }

        let containerView = ctx.containerView
        let toVC = ctx.viewController(forKey: .to) as! FirstViewController
        let fromVC = ctx.viewController(forKey: .from) as! SecondViewController

        containerView.insertSubview(toVC.view, belowSubview: fromVC.view)

        self.animator = UIViewPropertyAnimator(duration: transitionDuration(using: ctx),
                                          curve: .easeOut, animations: {

            self.fromVC.view.transform = CGAffineTransform(scale: 0.5)

        })

        self.animator.isInterruptible = true
        self.animator.isUserInteractionEnabled = true
        self.animator.isManualHitTestingEnabled = true

        self.animator.addCompletion { position in
            switch position {
            case .end:
                break
            case .current:
                break
            case .start:
                break
            }
            let cancelled = ctx.transitionWasCancelled
            if (cancelled) {
                //..
            } else {
                //..
            }
            ctx.completeTransition(!cancelled)
        }
        self.animator = anim
        return self.animator
    }

    func transitionDuration(using ctx: UIViewControllerContextTransitioning?) -> TimeInterval {
        return 0.5
    }

    func animateTransition(using ctx: UIViewControllerContextTransitioning) {
        let animator = self.interruptibleAnimator(using: ctx)
        self.animator.startAnimation()
    }

    func animationEnded(_ transitionCompleted: Bool) {
        self.interactiveTransition = nil
        self.animator = nil
    }
}

Pan Gesture to handle the animation:

func handlePanGesture(gestureRecognizer: UIPanGestureRecognizer) {

    let panTranslation = gestureRecognizer.translation(in: gestureRecognizer.view!)
    var progress = panTranslation.y / (gestureRecognizer.view!.bounds.size.height * 0.5)

    switch gestureRecognizer.state {
    case .began:

        self.interactiveTransition = UIPercentDrivenInteractiveTransition()
        self.navigationController!.popViewController(animated: true)

    case .changed:
        self.interactiveTransition!.update(progress)

    case .cancelled, .ended:

        if progress > 0.5 {
            //Complete Transition
            let timingParameters = UICubicTimingParameters(animationCurve: .easeInOut)
            self.animator!.continueAnimation!(withTimingParameters: timingParameters, durationFactor: progress)
            self.animator?.addAnimations! {
                //Completion Animations

            }
            self.interactiveTransition!.finish()

        } else {
            //Cancel Transition
            self.animator!.isReversed = true
            let timingParameters = UICubicTimingParameters(animationCurve: .easeInOut)
            self.animator!.continueAnimation!(withTimingParameters: timingParameters, durationFactor: progress)
            self.animator!.addAnimations!({
                //Cancelling Animations

            }, delayFactor: 0 )
            self.interactiveTransition!.cancel()
        }

    default:
        break
    }
}

What Works

Swiping down to dismissal works perfectly. Swiping slightly down and lifting finger to cancel also works perfectly.

Issue

Swiping down and back up beyond starting point (where progress becomes negative) and lifting up the finger should cancel the transition with cancelling animation. This happens in iOS 10 but it first reverses the navigation controller transitions first, then snaps back. In iOS 11, cancelling animation happens, then I see navigation controller transition is reversed. If you wait, you can see navigation controller transition does try to correct it self in animation over 10 mins or so.

Issue with:

 - self.interactiveTransition!.cancel()?
 - self.interactiveTransition!.completionSpeed ??
Pym answered 30/7, 2017 at 15:17 Comment(2)
Please attach a sample project highlighting the issue.Degradation
Yes, very similar problem here. Were you able to figure out this one?Loredo
C
3

I don't know if this is a bug or we're all just doing it wrong but to correct the behavior, add .completionSpeed = 0.999 to the interactionController in the .ended case of the pan gesture handler. It's a hack but at least it's only a single line.

Calc answered 14/12, 2017 at 18:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.