为什么UIView.animate使用交互式控制器转换,但UIViewPropertyAnimator不支持?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了为什么UIView.animate使用交互式控制器转换,但UIViewPropertyAnimator不支持?相关的知识,希望对你有一定的参考价值。
有关设置手势识别器的样板文件以及交互式过渡,请参阅this answer。
我正在尝试交互式过渡,并花了相当多的时间试图弄清楚为什么控制器会正常转换而不是根据手势擦洗。我发现它没有用,因为我使用的是UIViewPropertyAnimator
。切换到较旧的UIView动画块开箱即用。为什么?实施有什么区别?
func animateTransition(using transitionContext: UIViewControllerContextTransitioning)
{
// Ignore the forced unwrapping, for sake of brevity.
let view_From = transitionContext.viewController(forKey: .from)!.view!
let view_To = transitionContext.viewController(forKey: .to)!.view!
transitionContext.containerView.insertSubview(view_To, aboveSubview: view_From)
view_To.alpha = 0
// This animation block works - it will follow the progress value of the interaction controller
UIView.animate(withDuration: 1, animations: {
view_From.alpha = 0.0
view_To.alpha = 1.0
}, completion: { finished in
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
})
// This animation block fails - it will play out normally and not be interactive
/*
let animator = UIViewPropertyAnimator(duration: 1, curve: .linear)
animator.addAnimations {
view_To.alpha = 1
view_From.alpha = 0
}
animator.addCompletion { (position) in
switch position {
case .end: print("Completion handler called at end of animation")
case .current: print("Completion handler called mid-way through animation")
case .start: print("Completion handler called at start of animation")
}
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
}
animator.startAnimation()
*/
}
随着ios 10中UIViewPropertyAnimator
的推出,UIViewControllerAnimatedTransitioning
协议也得到了更新。他们添加了一个你不必实现的可选func interruptibleAnimator(using: UIViewControllerContextTransitioning)
(我想是为了向后兼容)。但它完全添加到你在这里提到的用例:利用新的UIViewPropertyAnimator
。
所以要得到你想要的东西:首先,你必须实现interruptibleAnimator(using:)
来创建动画师 - 你不要在animateTransition(using:)
中创建它。
根据UIViewControllerAnimatedTransitioning
的源代码中的评论(重点是我的)(我不知道为什么文档不包含这个信息):
如果可以中断它创建的转换,则符合对象会实现此方法。例如,它可以返回UIViewPropertyAnimator的实例。预计此方法将在转换的生命周期内返回相同的实例。
您必须在转换期间返回相同的动画师。这就是你会发现的原因
private var animatorForCurrentSession: UIViewImplicitlyAnimating?
在我的BackAnimator
实现中的属性 - 如果转换尚未结束,我将当前的动画存储在那里以返回它。
当interruptibleAnimator(using:)
实现时,环境将采用该动画师并使用它而不是使用animateTransition(using:)
制作动画。但是为了保持协议的契约,animateTransition(using:)
应该能够为过渡设置动画 - 但你可以简单地使用interruptibleAnimator(using:)
创建一个动画师并在那里运行动画。
以下是一个有效的BackAnimator
实现,您可以使用this SO question中引用的示例。我使用你的代码作为基础,但你可以简单地交换我的BackAnimator
实现它们你很高兴(我在他们的例子中测试它)。
class BackAnimator : NSObject, UIViewControllerAnimatedTransitioning {
// property for keeping the animator for current ongoing transition
private var animatorForCurrentTransition: UIViewImplicitlyAnimating?
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.5
}
func interruptibleAnimator(using transitionContext: UIViewControllerContextTransitioning) -> UIViewImplicitlyAnimating {
// as per documentation, the same object should be returned for the ongoing transition
if let animatorForCurrentSession = animatorForCurrentTransition {
return animatorForCurrentSession
}
// normal creation of the propertyAnimator
let view_From = transitionContext.viewController(forKey: .from)!.view!
let view_To = transitionContext.viewController(forKey: .to)!.view!
transitionContext.containerView.insertSubview(view_To, aboveSubview: view_From)
view_To.alpha = 0
let animator = UIViewPropertyAnimator(duration: transitionDuration(using: transitionContext), curve: .linear)
animator.addAnimations {
view_To.alpha = 1
view_From.alpha = 0
}
animator.addCompletion { (position) in
switch position {
case .end: print("Completion handler called at end of animation")
case .current: print("Completion handler called mid-way through animation")
case .start: print("Completion handler called at start of animation")
}
// transition completed, reset the current animator:
self.animatorForCurrentTransition = nil
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
}
// keep the reference to current animator
self.animatorForCurrentTransition = animator
return animator
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
// animateTransition should work too, so let's just use the interruptibleAnimator implementation to achieve it
let anim = self.interruptibleAnimator(using: transitionContext)
anim.startAnimation()
}
}
另请注意,interruptibleAnimator(using:)
返回的动画师不是由我们启动的 - 环境将在适当的时候启动它。
P.S。:我对这个主题的大部分知识来自于试图实现一个开源容器,它允许在其容器之间进行自定义交互式转换 - InteractiveTransitioningContainer。也许你会发现一些灵感:)。
以上是关于为什么UIView.animate使用交互式控制器转换,但UIViewPropertyAnimator不支持?的主要内容,如果未能解决你的问题,请参考以下文章
当使用 UIView.animate 动画视图时,其他视图也不需要动画
UIView.animate() 无法为视图设置动画的各种原因都有哪些?
NSLayoutConstraint 不使用 UIView.animate 进行动画处理
当按下按钮但卡住时,尝试在UIView.animate中更改按钮标题和label.center.x.为什么? Swift 4 Xcode 9.4