Xcode 10:自定义动画过渡卡住
Xcode 10: Custom Animated Transitions Stuck
我的应用程序有两个视图控制器(VC A 和 B)和它们之间的自定义转换。
在 VC A 上使用向左平移手势时,交互式动画过渡以模态方式呈现 VC B 在 A 上从右侧(从右到左)滑入。
要关闭 VC B,用户可以:
- 使用向右平移手势:将触发交互式过渡,将VC B向右滑动并揭开VC A。确定VC B的位置通过平移手势状态交互。 'driven' 由
UIPercentDrivenInteractiveTransition
个对象进行交互。
- 在 VC B 导航栏上使用 "close" 按钮。这将触发自定义过渡(向右滑动),没有交互(只是动画)。
问题是在 Xcode 10 Seed(构建 10A254a)+ iOS 12 模拟器(X 或 XR 或 XS)上进行测试 自定义转换永远不会完成并且 UI 处于一种奇怪的状态 :
- UI 卡在 VC B 上,手势或点击都不起作用。
- 应用程序未卡住 - 我可以看到控制台日志仍在滚动并且网络 activity 正在运行(日志中没有错误)
- 在这种卡住状态下暂停应用程序我可以看到 com.apple.main-线程没有卡住。
- 当我点击 "Debug View Hierarchy" 时,奇怪的事情发生了:在 sim 屏幕上我仍然可以看到 VC B 并且所有 UI 都被禁用 。在视图调试器主视图上 - 我可以看到 VC A 的子视图 绘制得好像转换已完成。在视图调试器左侧树视图中 - 我可以看到 VC B.
的 view hierarcgy
此问题从未出现在 Xcode and/or iOS pre Xcode 10/iOS12.
之前的任何版本中
这是我自定义的 animateTransition
方法 UIViewControllerAnimatedTransitioning
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
guard let fromVC = transitionContext.viewController(forKey: .from), let toVC = transitionContext.viewController(forKey: .to) else {
transitionContext.completeTransition(false)
return
}
let containterView = transitionContext.containerView
containterView.insertSubview(toVC.view, belowSubview: fromVC.view)
let bounds = fromVC.view.bounds
var xOffsetMultiplier : CGFloat = 0.0
var yOffsetMultiplier : CGFloat = 0.0
switch direction {
case .up:
yOffsetMultiplier = -1.0
case .right:
xOffsetMultiplier = 1.0
case .left:
xOffsetMultiplier = -1.0
case .down:
yOffsetMultiplier = 1.0
}
print(xOffsetMultiplier,bounds.size.width,bounds.size.height )
UIView.animate(withDuration: duration, animations: {
print("animating...")
//fromVC.navigationController?.navigationBar.alpha = 0.0
fromVC.view.frame = fromVC.view.frame.offsetBy(dx: xOffsetMultiplier * bounds.size.width, dy: yOffsetMultiplier * bounds.size.height)
}, completion: { finished in
print("completed animation")
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
//fromVC.navigationController?.navigationBar.alpha = 1.0
})
}
那里的印刷品只是为了调试。
这是很容易重现问题的序列:
- 使用平移手势启动从 B 回到 A 的交互式过渡,但永远不会完成它 - 这将在
UIPercentDrivenInteractiveTransition
对象上调用 cancel()
+ 我可以验证动画是否已完成。
- 点击 'close' 按钮调用非交互式过渡以关闭 B。B 永远不会关闭,自定义动画永远不会完成!
在设备上我根本无法重现此问题(目前)- 所有转换都按预期工作。
所以找到了为什么转换卡在 VC B 和 A 之间的原因,但仍然不真正理解 Xcode9/iOS11 和 Xcode10/iOS12 之间产生不同行为的区别。
为了简短起见:
- 当使用平移手势启动交互式转换以关闭 VC B 时,我分配了一个
UIPercentDrivenInteractiveTransition
,在 VC 上调用 dismiss(animated:completion:)
并根据泛进步。在某些情况下,当平移没有足够遍历时 "ground" 我的手势处理程序认为转换已取消并调用 UIPercentDrivenInteractiveTransition
的 cancel()
方法
- 在这样的取消之后,点击关闭按钮会启动一个新的
dismiss(animated:completion:)
但是因为 UIPercentDrivenInteractiveTransition
仍然被分配,它被我的转换委托返回并且 OS 实际上尝试了一个交互式解雇,尽管这不是本意。这是我的一个错误,因为在调用 cancel
之后我还应该确保在这种情况下转换委托不会尝试交互式转换(尽管在 Xcode9/iOS11 上它没有)。
- 过渡是 'stuck' 的原因是因为它是一个没有更新的交互式过渡(点击 [=40= 时没有手势更新)。我通过错误地强制执行
finish()
来验证这一点分配 UIPercentDrivenInteractiveTransition
所以它完成,一切都恢复正常。
根据用户交互确保关闭过渡是交互的或非交互的,尤其是在取消交互之后,解决了这个问题。
我不明白的是为什么 Xcode/iOS 版本之间的行为不一致。这个问题以前从未在任何设备或模拟器上发生过。
自定义 animations/transitions 的处理方式有所不同 - Apple 文档中没有任何内容可以解释这一点 - 可能在过渡上下文的内部实现中。
从天真的"eye-test"看来,Xcode10模拟器上的过渡动画反应时间较慢,而且不如以前流畅,但仍然没有完全解释它。
尝试在您的 UIPercentDrivenInteractiveTransition
中设置 wantsInteractiveStart = false
我的应用程序有两个视图控制器(VC A 和 B)和它们之间的自定义转换。
在 VC A 上使用向左平移手势时,交互式动画过渡以模态方式呈现 VC B 在 A 上从右侧(从右到左)滑入。 要关闭 VC B,用户可以:
- 使用向右平移手势:将触发交互式过渡,将VC B向右滑动并揭开VC A。确定VC B的位置通过平移手势状态交互。 'driven' 由
UIPercentDrivenInteractiveTransition
个对象进行交互。 - 在 VC B 导航栏上使用 "close" 按钮。这将触发自定义过渡(向右滑动),没有交互(只是动画)。
问题是在 Xcode 10 Seed(构建 10A254a)+ iOS 12 模拟器(X 或 XR 或 XS)上进行测试 自定义转换永远不会完成并且 UI 处于一种奇怪的状态 :
- UI 卡在 VC B 上,手势或点击都不起作用。
- 应用程序未卡住 - 我可以看到控制台日志仍在滚动并且网络 activity 正在运行(日志中没有错误)
- 在这种卡住状态下暂停应用程序我可以看到 com.apple.main-线程没有卡住。
- 当我点击 "Debug View Hierarchy" 时,奇怪的事情发生了:在 sim 屏幕上我仍然可以看到 VC B 并且所有 UI 都被禁用 。在视图调试器主视图上 - 我可以看到 VC A 的子视图 绘制得好像转换已完成。在视图调试器左侧树视图中 - 我可以看到 VC B. 的 view hierarcgy
此问题从未出现在 Xcode and/or iOS pre Xcode 10/iOS12.
之前的任何版本中这是我自定义的 animateTransition
方法 UIViewControllerAnimatedTransitioning
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
guard let fromVC = transitionContext.viewController(forKey: .from), let toVC = transitionContext.viewController(forKey: .to) else {
transitionContext.completeTransition(false)
return
}
let containterView = transitionContext.containerView
containterView.insertSubview(toVC.view, belowSubview: fromVC.view)
let bounds = fromVC.view.bounds
var xOffsetMultiplier : CGFloat = 0.0
var yOffsetMultiplier : CGFloat = 0.0
switch direction {
case .up:
yOffsetMultiplier = -1.0
case .right:
xOffsetMultiplier = 1.0
case .left:
xOffsetMultiplier = -1.0
case .down:
yOffsetMultiplier = 1.0
}
print(xOffsetMultiplier,bounds.size.width,bounds.size.height )
UIView.animate(withDuration: duration, animations: {
print("animating...")
//fromVC.navigationController?.navigationBar.alpha = 0.0
fromVC.view.frame = fromVC.view.frame.offsetBy(dx: xOffsetMultiplier * bounds.size.width, dy: yOffsetMultiplier * bounds.size.height)
}, completion: { finished in
print("completed animation")
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
//fromVC.navigationController?.navigationBar.alpha = 1.0
})
}
那里的印刷品只是为了调试。
这是很容易重现问题的序列:
- 使用平移手势启动从 B 回到 A 的交互式过渡,但永远不会完成它 - 这将在
UIPercentDrivenInteractiveTransition
对象上调用cancel()
+ 我可以验证动画是否已完成。 - 点击 'close' 按钮调用非交互式过渡以关闭 B。B 永远不会关闭,自定义动画永远不会完成!
在设备上我根本无法重现此问题(目前)- 所有转换都按预期工作。
所以找到了为什么转换卡在 VC B 和 A 之间的原因,但仍然不真正理解 Xcode9/iOS11 和 Xcode10/iOS12 之间产生不同行为的区别。
为了简短起见:
- 当使用平移手势启动交互式转换以关闭 VC B 时,我分配了一个
UIPercentDrivenInteractiveTransition
,在 VC 上调用dismiss(animated:completion:)
并根据泛进步。在某些情况下,当平移没有足够遍历时 "ground" 我的手势处理程序认为转换已取消并调用UIPercentDrivenInteractiveTransition
的 - 在这样的取消之后,点击关闭按钮会启动一个新的
dismiss(animated:completion:)
但是因为UIPercentDrivenInteractiveTransition
仍然被分配,它被我的转换委托返回并且 OS 实际上尝试了一个交互式解雇,尽管这不是本意。这是我的一个错误,因为在调用cancel
之后我还应该确保在这种情况下转换委托不会尝试交互式转换(尽管在 Xcode9/iOS11 上它没有)。 - 过渡是 'stuck' 的原因是因为它是一个没有更新的交互式过渡(点击 [=40= 时没有手势更新)。我通过错误地强制执行
finish()
来验证这一点分配UIPercentDrivenInteractiveTransition
所以它完成,一切都恢复正常。
cancel()
方法
根据用户交互确保关闭过渡是交互的或非交互的,尤其是在取消交互之后,解决了这个问题。
我不明白的是为什么 Xcode/iOS 版本之间的行为不一致。这个问题以前从未在任何设备或模拟器上发生过。 自定义 animations/transitions 的处理方式有所不同 - Apple 文档中没有任何内容可以解释这一点 - 可能在过渡上下文的内部实现中。
从天真的"eye-test"看来,Xcode10模拟器上的过渡动画反应时间较慢,而且不如以前流畅,但仍然没有完全解释它。
尝试在您的 UIPercentDrivenInteractiveTransition
中设置wantsInteractiveStart = false