【问题标题】:Using UIPercentDrivenInteractiveTransition with CABasicAnimation has weird glitch将 UIPercentDrivenInteractiveTransition 与 CABasicAnimation 一起使用会出现奇怪的故障
【发布时间】:2019-08-27 20:03:06
【问题描述】:

我正在使用 CABasicAnimationUIView.animate 来实现自定义过渡。还需要使用 UIPercentDrivenInteractiveTransition 来实现自定义交互过渡,该过渡完全复制本机 iOS 向后滑动的行为。没有向后滑动手势的动画(当我通过向后箭头推动和弹出时)工作正常且流畅。此外,向后滑动也很流畅,除非手势速度超过 900

手势识别功能:

@objc func handleBackGesture(_ gesture: UIScreenEdgePanGestureRecognizer) {
        guard animationTransition != nil else { return }
        switch gesture.state {
        case .began:
            interactionController = TransparentNavigationControllerTransitionInteractor(duration: anumationDuration)
            popViewController(animated: true)

        case .changed:
            guard let view = gesture.view?.superview else { return }
            let translation = gesture.translation(in: view)
            var percentage = translation.x / view.bounds.size.width
            percentage = min(1.0, max(0.0, percentage))
            shouldCompleteTransition = percentage > 0.5
            interactionController?.update(percentage)

        case .cancelled, .failed, .possible:
            if let interactionController = self.interactionController {
                isInteractiveStarted = false
                interactionController.cancel()
            }

        case .ended:
            interactionController?.completionSpeed = 0.999
            let greaterThanMaxVelocity = gesture.velocity(in: view).x > 800
            let canFinish = shouldCompleteTransition || greaterThanMaxVelocity
            canFinish ? interactionController?.finish() : interactionController?.cancel()
            interactionController = nil

        @unknown default: assertionFailure()
        }
    }

UIPercentDrivenInteractiveTransition 类。这里我正在同步图层动画。

final class TransparentNavigationControllerTransitionInteractor: UIPercentDrivenInteractiveTransition {

    // MARK: - Private Properties

    private var context: UIViewControllerContextTransitioning?
    private var pausedTime: CFTimeInterval = 0
    private let animationDuration: TimeInterval

    // MARK: - Initialization

    init(duration: TimeInterval) {
        self.animationDuration = duration * 0.4 // I dk why but layer duration should be less 
        super.init()
    }

    // MARK: - Public Methods

    override func startInteractiveTransition(_ transitionContext: UIViewControllerContextTransitioning) {
        super.startInteractiveTransition(transitionContext)
        context = transitionContext

        pausedTime = transitionContext.containerView.layer.convertTime(CACurrentMediaTime(), from: nil)
        transitionContext.containerView.layer.speed = 0
        transitionContext.containerView.layer.timeOffset = pausedTime
    }

    override func finish() {
        restart(isFinishing: true)
        super.finish()
    }

    override func cancel() {
        restart(isFinishing: false)
        super.cancel()
    }

    override func update(_ percentComplete: CGFloat) {
        super.update(percentComplete)
        guard let transitionContext = context else { return }
        let progress = CGFloat(animationDuration) * percentComplete
        transitionContext.containerView.layer.timeOffset = pausedTime + Double(progress)
    }

    // MARK: - Private Methods 

    private func restart(isFinishing: Bool) {
        guard let transitionLayer = context?.containerView.layer else { return }
        transitionLayer.beginTime = transitionLayer.convertTime(CACurrentMediaTime(), from: nil)
        transitionLayer.speed = isFinishing ? 1 : -1
    }
}

这是我在 UIViewControllerAnimatedTransitioning 类中的解雇动画函数


private func runDismissAnimationFrom(
        _ fromView: UIView,
        to toView: UIView,
        in transitionContext: UIViewControllerContextTransitioning) {

        guard let toViewController = transitionContext.viewController(forKey: .to) else { return }
        toView.frame = toView.frame.offsetBy(dx: -fromView.frame.width / 3, dy: 0)

        let toViewFinalFrame = transitionContext.finalFrame(for: toViewController)
        let fromViewFinalFrame = fromView.frame.offsetBy(dx: fromView.frame.width, dy: 0)

        // Create mask to hide bottom view with sliding
        let slidingMask = CAShapeLayer()
        let initialMaskPath = UIBezierPath(rect: CGRect(
            x: fromView.frame.width / 3,
            y: 0,
            width: 0,
            height: toView.frame.height)
        )
        let finalMaskPath = UIBezierPath(rect: toViewFinalFrame)
        slidingMask.path = initialMaskPath.cgPath
        toView.layer.mask = slidingMask
        toView.alpha = 0

        let slidingAnimation = CABasicAnimation(keyPath: "path")
        slidingAnimation.fromValue = initialMaskPath.cgPath
        slidingAnimation.toValue = finalMaskPath.cgPath
        slidingAnimation.timingFunction = .init(name: .linear)
        slidingMask.path = finalMaskPath.cgPath
        slidingMask.add(slidingAnimation, forKey: slidingAnimation.keyPath)

        UIView.animate(
            withDuration: duration,
            delay: 0,
            options: animationOptions,
            animations: {
                fromView.frame = fromViewFinalFrame
                toView.frame = toViewFinalFrame
                toView.alpha = 1
        },
            completion: { _ in
                toView.layer.mask = nil
                transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
        })
    }

我注意到只有当滑动速度很大时才会出现故障。 这里是正常速度下动画流畅而高速下不流畅的视频 - https://youtu.be/1d-kTPlhNvE

UPD: 我已经尝试将 UIViewPropertyAnimatorinterruptibleAnimator(使用transitionContext: UIViewControllerContextTransitioning) -> UIViewImplicitlyAnimating

但结果是另一种类型的故障。

【问题讨论】:

    标签: swift core-animation calayer cabasicanimation uiviewanimationtransition


    【解决方案1】:

    我已经解决了这个问题,只需更改restart功能的一部分:

            transitionLayer.beginTime = 
    transitionLayer.convertTime(CACurrentMediaTime(), from: nil) - transitionLayer.timeOffset
            transitionLayer.speed = 1
    

    我不太明白为什么,但看起来 timeOffset 减法有效!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-06-07
      • 2021-11-01
      • 2020-08-03
      • 1970-01-01
      • 2016-04-08
      • 2016-01-04
      相关资源
      最近更新 更多