Ios 当UIPercentDrivenInteractiveTransition结束一半时触发取消动画

Ios 当UIPercentDrivenInteractiveTransition结束一半时触发取消动画,ios,uiviewanimationtransition,Ios,Uiviewanimationtransition,如果交互结束且动画被取消,强制视图控制器的自定义转换回到原始位置是否是一种良好的做法。我有下面的实现,它决定何时关闭“平移”手势上的视图控制器。如果平移手势提前结束,我希望在考虑持续时间与平移手势的进度值成比例之前,将动画设置回原始位置,就像它的显示方式一样 protocol AnimationControllerDelegate: AnyObject { func shouldHandlePanelInteractionGesture() -> Bool } typealias P

如果交互结束且动画被取消,强制视图控制器的自定义转换回到原始位置是否是一种良好的做法。我有下面的实现,它决定何时关闭“平移”手势上的视图控制器。如果平移手势提前结束,我希望在考虑持续时间与平移手势的<代码>进度值成比例之前,将动画设置回原始位置,就像它的显示方式一样

protocol AnimationControllerDelegate: AnyObject {
  func shouldHandlePanelInteractionGesture() -> Bool
}

typealias PanGestureHandler = AnimationControllerDelegate & UIViewController & Animatable

final class CustomInteractionController: UIPercentDrivenInteractiveTransition, UIGestureRecognizerDelegate {
    var interactionInProgress: Bool = false
    private var shouldCompleteTransition: Bool = false
    private var startTransitionY: CGFloat = 0
    private var panGestureRecognizer: UIPanGestureRecognizer?
    private weak var viewController: PanGestureHandler?

    func wireToViewController(viewController: Any) {
        guard let viewControllerDelegate = viewController as? PanGestureHandler else {
            return
        }

        self.viewController = viewControllerDelegate

        let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePanGestureRecognizer(_:)))

        panGestureRecognizer = panGesture
        panGestureRecognizer?.delegate  = self

        self.viewController?.view.addGestureRecognizer(panGesture)
    }

    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer,
                           shouldRecognizeSimultaneouslyWith
        otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        return true
    }

    @objc
    func handlePanGestureRecognizer(_ gestureRecognizer: UIPanGestureRecognizer) {
        guard let childView = gestureRecognizer.view,
            let parentView = childView.superview,
            let panGestureHandler = viewController else {
            return
        }

        switch gestureRecognizer.state {
        case .began:
            break
        case .changed:
            let translation = gestureRecognizer.translation(in: parentView)
            let velocity = gestureRecognizer.velocity(in: parentView)
            let state = gestureRecognizer.state

            if !panGestureHandler.shouldHandlePanelInteractionGesture() && percentComplete == 0 {
                return
            }

            let verticalMovement = translation.y / childView.bounds.height
            let downwardMovement = fmaxf(Float(verticalMovement), 0.0)
            let downwardMovementPercent = fminf(downwardMovement, 1.0)
            let progress = CGFloat(downwardMovementPercent)

            let alphaValue = (1 - progress) * 0.4

            panGestureHandler.shadowView.backgroundColor = Safety.Colors.backgroundViewColor(for: alphaValue)

            if abs(velocity.x) > abs(velocity.y) && state == .began {
                return
            }

            if !interactionInProgress {
                interactionInProgress = true
                startTransitionY = translation.y
                viewController?.dismiss(animated: true, completion: nil)
            } else {
                shouldCompleteTransition = progress > 0.3
                update(progress)
            }
        case .cancelled:
            interactionInProgress = false
            startTransitionY = 0
            cancel()
        case .ended:
            interactionInProgress = false
            startTransitionY = 0
            if !shouldCompleteTransition {
                // Can I call a custom transition here back to original position?
                cancel()
            } else {
                finish()
            }
        case .failed:
            interactionInProgress = false
            startTransitionY = 0
            cancel()
        default:
            break
        }
    }
}
如果交互结束且动画被取消,强制视图控制器的自定义转换回到原始位置是否是一种良好的做法

是的,我认为如果用户取消手势,最好将其反转。但你不必“强迫”它。完成转换,只需指示是要完成还是要反转。因此,如果取消动画,它将自动反转并返回到自动为您设置的位置。您无需执行任何操作,只需取消

当然,这假定在animator中的动画完成块中,您在
completeTransition
中指示动画是否完成:

UIView.animate(withDuration: 0.25, animations: {
    ...
}, completion: { _ in
    transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
})
因此,如果没有取消,动画将完成。但如果它被取消,它将逆转


就个人而言,在手势识别器中,我通常会执行以下操作:

  • 如果手势
    状态
    开始
    ,我将:

    • 实例化
      UIViewControllerTransitionDelegate将从
      interactionControllerForPresentation(使用)
      返回的
      UIPercentDrivenInteractiveTransition
      ;及
    • 执行
      显示
      /
      解除
      UIPercentDrivenInteractiveTransition
  • 当手势更新为
    .changed
    状态时,只需
    更新
    UIPercentDrivenInteractiveTransition
    ;及

  • 完成后,您可以
    完成
    取消
    它。而
    cancel
    将自动触发反向动画

例如,我从左到右的“驳回”手势最终看起来像:

@objc func handleLeftToRightPan(_ gesture: UIPanGestureRecognizer) {
    let percent = (gesture.translation(in: gesture.view).x) / gesture.view!.bounds.width

    switch gesture.state {
    case .began:
        interactionController = UIPercentDrivenInteractiveTransition()
        dismiss(animated: true)
        interactionController?.update(percent)

    case .changed:
        interactionController?.update(percent)

    case .ended, .cancelled:
        let velocity = gesture.velocity(in: gesture.view).x
        if velocity > 0 || (velocity == 0 && percent > 0.5) {
            interactionController?.finish()
        } else {
            interactionController?.cancel()
        }
        interactionController = nil

    default:
        break
    }
}
就个人而言,对于
finish
cancel
逻辑,我检查是否:

  • 速度
    是否在手势的方向上(从左到右的“驳回”手势,这意味着我检查它是否为正
    x
    速度)。。。这意味着,如果用户点击,即使是屏幕的一小部分,我仍然会认为完全过渡的意图;
  • 只有当
    速度
    为零时,我才检查完成百分比(例如,如果他们走了¾个方向,停下来,然后放开,我假设他们想
    完成
    手势,但如果他们只走了¼个方向,停下来,然后放开,我假设他们打算取消手势

  • 不用说,如果他们扭转了他们的手势的方向,我认为他们完全转变的意图被取消了

但正如您所看到的,我只做了
cancel
,动画会自动反转。因此,在这里,我开始从第二个视图控制器返回到第一个视图控制器,但是
cancel
UIPercentDrivenInteractiveTransition
(在这种情况下,通过反转手势的方向并放开),我的动画制作者将在其完成处理程序中,将适当的
Bool
值传递给其完成处理程序中的
completeTransition
,并在我不做任何工作的情况下自动设置动画: