交互式视图控制器过渡-奇怪的行为(卡在过渡中)

时间:2019-01-11 14:07:20

标签: ios swift uigesturerecognizer

我正在尝试实现自定义pan手势以交互方式过渡到新的view controller。它的工作方式是,我有一个按钮(标为“模板编辑器”,见下文),您可以在其上启动一个pan,将当前的view controller向右移动,以显示新的{{1 }}旁边(我已经记录了我的问题,请参见下文)。

一切正常,但有一个我根本不了解的错误:

有时候,当我只需在按钮上滑动(触发view controller手势)然后再次抬起手指时(向下触摸->快速向右快速滑动->向上触摸)交互式过渡出现故障。它开始非常缓慢完成过渡,此后,我无法解雇提出的pan,也不能提出提出的view controller上的任何内容。

我不知道为什么。这是我的代码:

首先,是view controller类。它是使用UIViewControllerAnimatedTransitioning实现的,只是使用UIViewPropertyAnimator添加了动画:

transform

这是增加交互性的部分。我添加到按钮中的class MovingTransitionAnimator: NSObject, UIViewControllerAnimatedTransitioning { enum Direction { case left, right } // MARK: - Properties // ========== PROPERTIES ========== private var animator: UIViewImplicitlyAnimating? var duration = 0.6 var presenting = true var shouldAnimateInteractively: Bool = false public var direction: Direction = .left private var movingMultiplicator: CGFloat { return direction == .left ? -1 : 1 } // ==================== // MARK: - Initializers // ========== INITIALIZERS ========== // ==================== // MARK: - Overrides // ========== OVERRIDES ========== // ==================== // MARK: - Functions // ========== FUNCTIONS ========== func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { return duration } func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { let animator = interruptibleAnimator(using: transitionContext) animator.startAnimation() } func interruptibleAnimator(using transitionContext: UIViewControllerContextTransitioning) -> UIViewImplicitlyAnimating { // If the animator already exists, return it (important, see documentation!) if let animator = self.animator { return animator } // Otherwise, create the animator let containerView = transitionContext.containerView let fromView = transitionContext.view(forKey: .from)! let toView = transitionContext.view(forKey: .to)! if presenting { toView.frame = containerView.frame toView.transform = CGAffineTransform(translationX: movingMultiplicator * toView.frame.width, y: 0) } else { toView.frame = containerView.frame toView.transform = CGAffineTransform(translationX: -movingMultiplicator * toView.frame.width, y: 0) } containerView.addSubview(toView) let animator = UIViewPropertyAnimator(duration: duration, dampingRatio: 0.9, animations: nil) animator.addAnimations { if self.presenting { toView.transform = .identity fromView.transform = CGAffineTransform(translationX: -self.movingMultiplicator * toView.frame.width, y: 0) } else { toView.transform = .identity fromView.transform = CGAffineTransform(translationX: self.movingMultiplicator * toView.frame.width, y: 0) } } animator.addCompletion { (position) in // Important to set frame above (device rotation will otherwise mess things up) toView.transform = .identity fromView.transform = .identity if !transitionContext.transitionWasCancelled { self.shouldAnimateInteractively = false } self.animator = nil transitionContext.completeTransition(!transitionContext.transitionWasCancelled) } self.animator = animator return animator } // ==================== } 正在调用此方法。

UIPanGestureRecognizer

我还实现了所有必要的委托方法:

    public lazy var transitionAnimator: MovingTransitionAnimator = MovingTransitionAnimator()
    public lazy var interactionController = UIPercentDrivenInteractiveTransition()

    ...

    @objc private func handlePan(pan: UIPanGestureRecognizer) {
        let translation = pan.translation(in: utilityView)
        var progress = (translation.x / utilityView.frame.width)
        progress = CGFloat(fminf(fmaxf(Float(progress), 0.0), 1.0))

        switch pan.state {
        case .began:

            // This is a flag that helps me distinguish between when a user taps on the button and when he starts a pan
            transitionAnimator.shouldAnimateInteractively = true

            // Just a dummy view controller that's dismissing as soon as its been presented (the problem occurs with every view controller I use here)
            let vc = UIViewController()
            vc.view.backgroundColor = .red
            vc.transitioningDelegate = self
            present(vc, animated: true, completion: {
                self.transitionAnimator.shouldAnimateInteractively = false
                vc.dismiss(animated: true, completion: nil)
            })
        case .changed:
            interactionController.update(progress)
        case .cancelled:
            interactionController.cancel()
        case .ended:
            if progress > 0.55 || pan.velocity(in: utilityView).x > 600 
                interactionController.completionSpeed = 0.8
                interactionController.finish()
            } else {
                interactionController.completionSpeed = 0.8
                interactionController.cancel()
            }
        default:
            break
        }
    }

就是这样。它背后没有更多逻辑(我想;如果您需要更多信息,请告诉我),但是它仍然存在此错误。这是该错误的记录。您看不到我的触摸,但是我正在做的只是向下触摸->快速,不久向右滑动->向上触摸。在完成这一非常缓慢的过渡之后,我无法解开红色的 func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? { transitionAnimator.presenting = true return transitionAnimator } func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { transitionAnimator.presenting = false return transitionAnimator } func interactionControllerForPresentation(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? { guard let animator = animator as? MovingTransitionAnimator, animator.shouldAnimateInteractively else { return nil } return interactionController } func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? { guard let animator = animator as? MovingTransitionAnimator, animator.shouldAnimateInteractively else { return nil } return interactionController } 。它被卡在那里:

demonstration

这甚至是陌生人:

发生这种情况时,view controllerinteractionController.finish()都没有被调用(至少不是从我的interactionController.cancel()方法中被调用)。

此错误发生后,我检查了handlePan(_:)中的view hierarchy,并得到了这个信息:

view hierarchy

首先,它似乎停留在过渡中(一切仍在Xcode内)。

第二,在左侧,您会看到第一个UITransitionView(我从中开始过渡的那个)的views。但是,在图像上只有可见的红色view controller(即将呈现的红色)。

您知道发生了什么吗?在过去的3个小时中,我一直试图弄清楚这一点,但是我无法使其正常工作。我将不胜感激

谢谢!

编辑

好的,我找到了一种100%的时间复制它的方法。我还创建了一个独立的项目来演示该问题(结构有所不同,因为我尝试了很多事情,但结果仍然完全相同)

这是项目:https://github.com/d3mueller/InteractiveTransitionDemo2

如何重现该问题:

从右向左滑动,然后从左向右快速滑动。这将触发该错误。

当您从右向左快速滑动多次时,也会出现类似的错误。然后它将实际运行过渡并正确完成过渡(但它甚至不应该开始,因为从右向左移动可使view controller保持在0.0)

1 个答案:

答案 0 :(得分:0)

您可以尝试设置:

/// Set this to NO in order to start an interruptible transition non
/// interactively. By default this is YES, which is consistent with the behavior
/// before 10.0.
@property (nonatomic) BOOL wantsInteractiveStart NS_AVAILABLE_IOS(10_0);

NO上的interactionController

祝您好运,如果您能解决这个问题,我会感到好奇。