UIPercentDrivenInteractiveTransition无法在快速手势上完成动画

Nic*_*ico 5 uiviewcontroller ios swift

我创建了一个交互式过渡.我func animateTransition(transitionContext: UIViewControllerContextTransitioning)很正常,我得到了容器UIView,我添加了两个UIViewControllers,然后我做了动画更改UIView.animateWithDuration(duration, animations, completion).

我添加了一个UIScreenEdgePanGestureRecognizer来自我的UIViewController.它运作良好,除非我做一个非常快速的平底锅.

在最后一个场景中,应用程序没有响应,仍然是相同的UIViewController(转换似乎没有工作)但后台任务运行.当我运行时Debug View Hierarchy,我看到的是新的UIViewController而不是前一个,而前一个(至少是它UIView)代表它应该在转换结束时站立的位置.

我做了一些打印和检查点,从中我可以说,当问题发生时,动画的完成(我animateTransition方法中的那个)没有到达,所以我无法调用transitionContext.completeTransition方法来完成或不完成转换.

我也可以看到平底锅有时从UIGestureRecognizerState.Began直线走到UIGestureRecognizerState.Ended没经过UIGestureRecognizerState.Changed.

当它通过UIGestureRecognizerState.Changed,translation velocity留为每个相同UIGestureRecognizerState.Changed的状态.

编辑:

这是代码:

animateTransition 方法

func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
    self.transitionContext = transitionContext

    let containerView = transitionContext.containerView()
    let screens: (from: UIViewController, to: UIViewController) = (transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)!, transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)!)

    let parentViewController = presenting ? screens.from : screens.to
    let childViewController = presenting ? screens.to : screens.from

    let parentView = parentViewController.view
    let childView = childViewController.view

    // positionning the "to" viewController's view for the animation
    if presenting {
        offStageChildViewController(childView)
    }

    containerView.addSubview(parentView)    
    containerView.addSubview(childView)

    let duration = transitionDuration(transitionContext)

    UIView.animateWithDuration(duration, animations: {

        if self.presenting {
            self.onStageViewController(childView)
            self.offStageParentViewController(parentView)
        } else {
            self.onStageViewController(parentView)
            self.offStageChildViewController(childView)
        }}, completion: { finished in
            if transitionContext.transitionWasCancelled() {
                transitionContext.completeTransition(false)
            } else {
                transitionContext.completeTransition(true)
            }
    })
}
Run Code Online (Sandbox Code Playgroud)

手势和手势处理程序:

weak var fromViewController: UIViewController! {
    didSet {
        let screenEdgePanRecognizer = UIScreenEdgePanGestureRecognizer(target: self, action: "presentingViewController:")
        screenEdgePanRecognizer.edges = edge
        fromViewController.view.addGestureRecognizer(screenEdgePanRecognizer)
    }
}

func presentingViewController(pan: UIPanGestureRecognizer) {
    let percentage = getPercentage(pan)

    switch pan.state {

    case UIGestureRecognizerState.Began:
        interactive = true
        presentViewController(pan)

    case UIGestureRecognizerState.Changed:
        updateInteractiveTransition(percentage)

    case UIGestureRecognizerState.Ended:
        interactive = false

        if finishPresenting(pan, percentage: percentage) {
            finishInteractiveTransition()
        } else {
            cancelInteractiveTransition()
        }

    default:
        break
    }
}
Run Code Online (Sandbox Code Playgroud)

知道会发生什么吗?

编辑2:

以下是未公开的方法:

override func getPercentage(pan: UIPanGestureRecognizer) -> CGFloat {
    let translation = pan.translationInView(pan.view!)
    return abs(translation.x / pan.view!.bounds.width)
}

override func onStageViewController(view: UIView) {
    view.transform = CGAffineTransformIdentity
}

override func offStageParentViewController(view: UIView) {
    view.transform = CGAffineTransformMakeTranslation(-view.bounds.width / 2, 0)
}

override func offStageChildViewController(view: UIView) {
    view.transform = CGAffineTransformMakeTranslation(view.bounds.width, 0)
}

override func presentViewController(pan: UIPanGestureRecognizer) {
    let location = pan.locationInView((fromViewController as! MainViewController).tableView)
    let indexPath = (fromViewController as! MainViewController).tableView.indexPathForRowAtPoint(location)

    if indexPath == nil {
        pan.state = .Failed
        return
    }
    fromViewController.performSegueWithIdentifier("chartSegue", sender: pan)
}
Run Code Online (Sandbox Code Playgroud)
  • 我删除了"over"添加行=>没有修复它
  • 我添加updateInteractiveTransition.Beganin .Ended,in = =>没有修复它
  • 我在我的视图层上启用了shouldRasterize toViewController并且一直让它继续=>没有修复它

但问题是,在进行快速交互式手势时,为什么它的响应速度不够快

只要我的手指足够长,它实际上可以使用快速交互式手势.例如,如果我在超过(比方说)1cm时非常快速地平移,那就没关系.如果我在小于1厘米的小表面(再说一次)上快速平移,那就不行了

可能的候选者包括动画的视图太复杂(或具有阴影等复杂效果)

我也想过一个复杂的观点,但我认为我的观点并不复杂.有一堆按钮和标签,一个自定义UIControl作为分段,一个图表(一旦控制器出现就加载)和一个xib加载到viewController中.


好的,我刚刚创建了一个包含MINIMUM类和对象的项目,以便触发问题.因此,要触发它,您只需从右向左快速轻扫即可.

我注意到它第一次很容易工作,但如果你第一次正常拖动视图控制器,那么触发它会更加困难(甚至不可能?).在我的完整项目中,它并不重要.

Rob*_*Rob 10

当我诊断出这个问题时,我注意到手势的变化和结束的状态事件都在发生之前animateTransition发生.所以动画在它开始之前就被取消/完成了!

我尝试使用GCD动画同步队列,以确保更新UIPercentDrivenInterativeTransition之前不会发生`animate:

private let animationSynchronizationQueue = dispatch_queue_create("com.domain.app.animationsynchronization", DISPATCH_QUEUE_SERIAL)
Run Code Online (Sandbox Code Playgroud)

然后,我有一个实用程序方法来使用此队列:

func dispatchToMainFromSynchronizationQueue(block: dispatch_block_t) {
    dispatch_async(animationSynchronizationQueue) {
        dispatch_sync(dispatch_get_main_queue(), block)
    }
}
Run Code Online (Sandbox Code Playgroud)

然后我的手势处理程序确保更改和结束状态通过该队列路由:

func handlePan(gesture: UIPanGestureRecognizer) {
    switch gesture.state {

    case .Began:
        dispatch_suspend(animationSynchronizationQueue)
        fromViewController.performSegueWithIdentifier("segueID", sender: gesture)

    case .Changed:
        dispatchToMainFromSynchronizationQueue() {
            self.updateInteractiveTransition(percentage)
        }

    case .Ended:
        dispatchToMainFromSynchronizationQueue() {
            if isOkToFinish {
                self.finishInteractiveTransition()
            } else {
                self.cancelInteractiveTransition()
            }
        }

    default:
        break
    }
}
Run Code Online (Sandbox Code Playgroud)

所以,我有手势识别器的.Began状态暂停该队列,并且我让动画控制器恢复该队列animationTransition(确保只有在该方法运行之后,手势才会尝试更新UIPercentDrivenInteractiveTransition对象之前,队列才会再次启动.