Interactive transition not linear

Viewed 918

I'm playing around with custom and interactive view controller transistion, with UIPercentDrivenInteractiveTransition. I'm building an app that present a card (other view controller) modally. I've made custom transistions with UIViewControllerAnimatedTransitioning that is animating the card view controller a lot like the standart model presentation style.

func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
    {...}
    let fullScreenFrame = transitionContext.finalFrame(for: cardVC)
    let offsetFrame = cardVC.view.frame.offsetBy(dx: 0, dy: cardVC.view.frame.height)
    let finalFrame = presenting ? fullScreenFrame : offsetFrame
    cardVC.view.frame = presenting ? offsetFrame : fullScreenFrame
    containerView.addSubview(cardVC.view)

    UIView.animateKeyframes(
        withDuration: transitionDuration(using: transitionContext),
        delay: 0,
        options: UIViewKeyframeAnimationOptions.calculationModeLinear,
        animations: {
            UIView.addKeyframe(
                withRelativeStartTime: 0,
                relativeDuration: 1,
                animations: { [weak self] in
                    guard let strongSelf = self else { return }
                    backgroundView.alpha = strongSelf.presenting ? 1 : 0
            })

            UIView.addKeyframe(
                withRelativeStartTime: 0,
                relativeDuration: 1,
                animations: {
                    cardVC.view.frame = finalFrame
            })
    }, completion: { finished in
        backgroundView.removeFromSuperview()
        gradientView.alpha = 1
        transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
    })
}

And then I use a pan gesture recognizer to interactively drive the dismiss animation:

func handleGesture(_ gestureRecognizer: UIScreenEdgePanGestureRecognizer) {
        let translation = gestureRecognizer.translation(in: gestureRecognizer.view)
        var progress = (translation.y / (UIScreen.main.bounds.height - 70))
        progress = CGFloat(fminf(fmaxf(Float(progress), 0.0), 1.0))

        switch gestureRecognizer.state {
        case .began:
            interactionInProgress = true
            viewController.dismiss(animated: true, completion: nil)

        case .changed:
            shouldCompleteTransition = progress > 0.35
            update(progress)

        case .cancelled:
            interactionInProgress = false
            cancel()

        case .ended:
            interactionInProgress = false
            if !shouldCompleteTransition {
                cancel()
            } else {
                finish()
            }

        default:
            print("Unsupported")
        }
    }

When I dismiss the presented view controller by dragging it down, it doesn't seem to move linearly with the gesture. It seems like the Interaction Controller is using some easeInEaseOut function. Maybe setting UIPercentDrivenInteractiveTransition's timingCurve to make the transition run linearly. Is that posible, or am I getting something wrong?

3 Answers

I was having the same issue, was able to set a custom animation curve for animateKeyframes using UIView.setAnimationCurve like this:

UIView.animateKeyframes(withDuration: 4, delay: 0, options: [], animations: {

    UIView.setAnimationCurve(.linear)

    UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 0.24, animations: {
        // Animations...
    })

    // Other key frames...
})

In my case, It works with interruptibleAnimator(using:) by return UIViewPropertyAnimator

Related