UIPercentDrivenInteractiveTransition无法通过快速手势

时间:2015-07-17 00:26:44

标签: ios swift uiviewcontroller

我创建了一个互动转换。我的func animateTransition(transitionContext: UIViewControllerContextTransitioning)很正常,我收到了容器UIView,我添加了两个UIViewControllers然后我在UIView.animateWithDuration(duration, animations, completion)中进行了动画更改。

我从UIScreenEdgePanGestureRecognizerUIViewController添加UIViewController。它运作良好,除非我做一个非常快速的平底锅。

在最后一个场景中,应用程序没有响应,仍然在同一个Debug View Hierarchy(过渡似乎没有奏效)但后台任务运行。当我运行UIViewController时,我会看到新的UIView而不是之前的animateTransition,前一个(至少它的transitionContext.completeTransition)代表它应该站在最后的位置转型。

我做了一些打印和检查点,从中我可以说当问题发生时,动画完成(我的UIGestureRecognizerState.Began方法中的那个)< strong>未达到,因此我无法调用UIGestureRecognizerState.Ended方法来完成转换。

我也可以看到平底锅有时会从UIGestureRecognizerState.Changed直接转到UIGestureRecognizerState.Changed而不经过translation

velocity UIGestureRecognizerState.Changed animateTransitionfunc 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) } }) } 保持不变时国家。

编辑:

以下是代码:

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 } } 方法

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)
}

手势和手势处理程序:

updateInteractiveTransition

知道会发生什么吗?

编辑2:

以下是未公开的方法:

.Began
  • 我删除&#34; over&#34;添加行=&gt;没有解决它
  • 我在.Ended中的toViewController中添加了UIControl,两个=&gt;没有解决它
  • 我在我的$('<canvas/>', { id: 'mycanvas', height: 500, width: 200}); 视图图层上启用了shouldRasterize并且一直让它开始=&gt;没有解决它
  

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

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

  

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

我也考虑过一个复杂的观点,但我认为我的观点并不复杂。有一堆按钮和标签,一个自定义{{1}}充当分段,一个图表(一旦控制器出现就加载),一个xib被加载到viewController中。

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

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

3 个答案:

答案 0 :(得分:10)

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

我尝试使用GCD动画同步队列,以确保在{animate:

之后才发生UIPercentDrivenInterativeTransition的更新。
private let animationSynchronizationQueue = dispatch_queue_create("com.domain.app.animationsynchronization", DISPATCH_QUEUE_SERIAL)

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

func dispatchToMainFromSynchronizationQueue(block: dispatch_block_t) {
    dispatch_async(animationSynchronizationQueue) {
        dispatch_sync(dispatch_get_main_queue(), block)
    }
}

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

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
    }
}

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

答案 1 :(得分:0)

遇到相同的问题,尝试使用serialQueue.suspend()/ resume(),不起作用。

此问题是因为当平移手势过快时,结束状态早于animateTransition开始,然后context.completeTransition无法运行,整个动画变得混乱了。

我的解决方案是在发生这种情况时强制运行context.completeTransition。

例如,我有两个班级:

class SwipeInteractor: UIPercentDrivenInteractiveTransition {
    var interactionInProgress = false

    ...
}

class AnimationController: UIViewControllerAnimatedTransitioning {
    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        if !swipeInteractor.interactionInProgress {
            DispatchQueue.main.asyncAfter(deadline: .now()+transitionDuration) {
                if context.transitionWasCancelled {
                    toView?.removeFromSuperview()
                } else {
                    fromView?.removeFromSuperview()
                }
                context.completeTransition(!context.transitionWasCancelled)
            }
        }

        ...
    }

    ...
}

interactionInProgress在手势开始时设置为true,在手势结束时设置为false。

答案 2 :(得分:0)

我有一个类似的问题,但是编程动画触发器没有触发动画完成块。我的解决方案就像Sam一样,除了不是在短暂的延迟后进行调度,而是在finish实例上手动调用UIPercentDrivenInteractiveTransition

class SwipeInteractor: UIPercentDrivenInteractiveTransition {
  var interactionInProgress = false
  ...
}

class AnimationController: UIViewControllerAnimatedTransitioning {
  private var swipeInteractor: SwipeInteractor
  ..
  func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
    ...
    if !swipeInteractor.interactionInProgress {
      swipeInteractor.finish()
    }
    ...
    UIView.animateWithDuration(...)
  }
}