Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UIPercentDrivenInteractiveTransition Cancelling Issue

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 ??
like image 715
Gizmodo Avatar asked Jul 30 '17 15:07

Gizmodo


1 Answers

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.

like image 52
liquid LFG UKRAINE Avatar answered Oct 30 '22 15:10

liquid LFG UKRAINE