UIPercentDrivenInteractiveTransition无法在快速手势上完成动画 [英] UIPercentDrivenInteractiveTransition doesn't get to animation's completion on fast gesture
问题描述
我创建了一个互动过渡。我的 func animateTransition(transitionContext:UIViewControllerContextTransitioning)
很正常,我得到容器 UIView
,我加了两个 UIViewControllers
然后我在 UIView.animateWithDuration(持续时间,动画,完成)
中进行动画更改。
I have created an interactive transition. My func animateTransition(transitionContext: UIViewControllerContextTransitioning)
is quite normal, I get the container UIView
, I add the two UIViewControllers
and then I do the animation changes in a UIView.animateWithDuration(duration, animations, completion)
.
我在 UIViewController
中添加 UIScreenEdgePanGestureRecognizer
。它运作良好,除非我做一个非常快速的平底锅。
I add a UIScreenEdgePanGestureRecognizer
to my from UIViewController
. It works well except when I do a very quick pan.
在最后一个场景中,应用程序没有响应,仍然在同一个 UIViewController
(转换似乎没有已经工作了)但后台任务运行。当我运行调试视图层次结构
时,我看到新的 UIViewController
而不是前一个和前一个(至少它的 UIView
)代表它应该在转换结束时站立的位置。
In that last scenario, the app is not responsive, still on the same UIViewController
(the transition seems not to have worked) but the background tasks run. When I run the Debug View Hierarchy
, I see the new UIViewController
instead of the previous one, and the previous one (at least its UIView
) stands where it is supposed to stand at the end of the transition.
我做了一些打印出来并检查点,我可以说,当问题发生时,动画完成(我的 animateTransition
方法中的那个)未到达,因此我无法调用 transitionContext.completeTransition
方法来完成或不完成转换。
I did some print out and check points and from that I can say that when the problem occurs, the animation's completion (the one in my animateTransition
method) is not reached, so I cannot call the transitionContext.completeTransition
method to complete or not the transition.
我也可以看到平底锅有时从 UIGestureRecognizerState.Began
直接到 UIGestureRecognizerState.Ended
而不去通过 UIGestureRecognizerState.Changed
。
I could see as well that the pan goes sometimes from UIGestureRecognizerState.Began
straight to UIGestureRecognizerState.Ended
without going through UIGestureRecognizerState.Changed
.
当它通过 UIGestureRecognizerState.Changed $ c时$ c>,
翻译
和每个 UIGestureRecognizerState.Changed
州的 velocity
保持不变。
When it goes through UIGestureRecognizerState.Changed
, both the translation
and the velocity
stay the same for every UIGestureRecognizerState.Changed
states.
编辑:
以下是代码:
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)
}
})
}
手势和手势处理程序:
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
}
}
知道会发生什么吗?
编辑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)
}
- 我删除了over添加行=>没有修复它
- 我在
中添加了
,在updateInteractiveTransition
.Began.Ended
中,两者都=>没有修复它 - 我开启了shouldRasterize我的
toViewController
的视图层,并让它一直开启=>没有修复它 - I remove the "over" adding lines => didn't fix it
- I added
updateInteractiveTransition
in.Began
, in.Ended
, in both => didn't fix it - I turned on shouldRasterize on the layer of the view of my
toViewController
and let it on all the time => didn't fix it
但问题是为什么在做快速交互式手势时,它没有足够快地响应
But the question is why, when doing a fast interactive gesture, is it not responding quickly enough
只要我的手指足够长,它实际上适用于快速交互式手势。例如,如果我在超过(比方说)1cm时非常快速地平移,那就没关系。如果我在一个小表面上快速平移(再说一次)不到1厘米就不行了。
It actually works with a fast interactive gesture as long as I leave my finger long enough. For example, if I pan very fast on more than (let say) 1cm, it's ok. It's not ok if I pan very fast on a small surface (let say again) less than 1cm
可能的候选人包括动画的视图是太复杂了(或者像阴影一样有复杂的效果)
Possible candidates include the views being animated are too complicated (or have complicated effects like shading)
我也考虑过一个复杂的视图,但我不认为我的观点是真的很复杂。有一堆按钮和标签,一个自定义 UIControl
充当分段,一个图表(一旦控制器出现就加载),并在viewController中加载一个xib 。
I thought about a complicated view as well but I don't think my view is really complicated. There are a bunch of buttons and labels, a custom UIControl
acting as a segmented segment, a chart (that is loaded once the controller appeared) and a xib is loaded inside the viewController.
好的我刚刚创建了一个项目,使用MINIMUM类和对象来触发问题。因此,要触发它,您只需从右向左快速轻扫即可。
Ok I just created a project with the MINIMUM classes and objects in order to trigger the problem. So to trigger it, you just do a fast and brief swipe from the right to the left.
我注意到它第一次非常容易,但是如果你第一次正常拖动视图控制器,那么触发它会更加困难(甚至不可能) ?)。在我的完整项目中,它并不重要。
What I noticed is that it works pretty easily the first time but if you drag the view controller normally the first time, then it get much harder to trigger it (even impossible?). While in my full project, it doesn't really matter.
推荐答案
当我诊断出这个问题时,我发现手势是更改和结束状态事件发生在 animateTransition
之前甚至已经运行。所以动画在它开始之前就被取消/完成了!
When I was diagnosing this problem, I noticed that the gesture's change and ended state events were taking place before animateTransition
even ran. So the animation was canceled/finished before it even started!
我尝试使用GCD动画同步队列来确保更新 UIPercentDrivenInterativeTransition
直到`animate:
I tried using GCD animation synchronization queue to ensure that the updating of the UIPercentDrivenInterativeTransition
doesn't happen until after `animate:
private let animationSynchronizationQueue = dispatch_queue_create("com.domain.app.animationsynchronization", DISPATCH_QUEUE_SERIAL)
然后我有一个实用工具方法来使用这个队列:
I then had a utility method to use this queue:
func dispatchToMainFromSynchronizationQueue(block: dispatch_block_t) {
dispatch_async(animationSynchronizationQueue) {
dispatch_sync(dispatch_get_main_queue(), block)
}
}
然后我的手势处理程序确保更改并结束状态通过该队列路由:
And then my gesture handler made sure that changes and ended states were routed through that queue:
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
中恢复该队列(确保队列在该方法运行之后再次启动,然后手势继续尝试更新 UIPercent DrivenInteractiveTransition
object。
So, I have the gesture recognizer's .Began
state suspend that queue, and I have the animation controller resume that queue in animationTransition
(ensuring that the queue starts again only after that method runs before the gesture proceeds to try to update the UIPercentDrivenInteractiveTransition
object.
这篇关于UIPercentDrivenInteractiveTransition无法在快速手势上完成动画的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!