UIViewPropertyAnimator调用stopAnimation后仍运行,触发finishAnimation崩溃
解决UIViewPropertyAnimator切换时的崩溃问题
这问题我之前也碰到过!核心原因是你对stopAnimation(_:)的参数行为理解有点偏差,导致调用时机不对触发了崩溃。
为什么调用stopAnimation(false)后动画器仍处于运行状态?
你调用oldValue.stopAnimation(false)时,这个方法并不是立即停止动画的——它的作用是让动画继续运行到当前的目标状态(比如交互式拖拽时的当前终点),然后才会异步切换到停止状态。所以在你调用完这个方法的瞬间,动画器其实还在运行中,这时候你立刻调用finishAnimation(at:)就会触发那个finishAnimationAtPosition: should only be called on a stopped animator!的崩溃,因为这个方法要求动画器必须处于「已停止」或「已暂停」的状态。
稳妥的解决方案
针对卡片滑动切换的场景,你需要在替换旧动画器时,确保它能被安全地终止并完成,推荐两种处理方式:
方式一:立即暂停并完成动画(适合快速切换场景)
这种方式会立刻暂停旧动画,然后直接跳转到动画结束状态,确保旧动画器不会在释放时引发崩溃:
fileprivate var runningAnimator: UIViewPropertyAnimator? { didSet { guard let oldAnimator = oldValue, oldAnimator.state != .finished else { return } // 如果动画正在运行,先立即暂停 if oldAnimator.isRunning { oldAnimator.pauseAnimation() } // 现在动画处于暂停状态,可以安全调用finishAnimation oldAnimator.finishAnimation(at: .end) } }
方式二:立即停止并完成动画(更直接的终止方式)
如果你不需要等待旧动画完成当前过渡,也可以用stopAnimation(true)立即终止动画,然后完成:
fileprivate var runningAnimator: UIViewPropertyAnimator? { didSet { guard let oldAnimator = oldValue, oldAnimator.state != .finished else { return } // 立即停止动画,进入stopped状态 oldAnimator.stopAnimation(true) // 安全完成动画 oldAnimator.finishAnimation(at: .end) } }
额外的双重保险:ViewController销毁时的清理
为了彻底避免释放时的崩溃,建议在VC的deinit方法里也加上清理逻辑:
deinit { guard let animator = runningAnimator, animator.state != .finished else { return } if animator.isRunning { animator.pauseAnimation() } animator.finishAnimation(at: .end) }
关键要点总结
stopAnimation(false)是异步完成停止的,不能在调用后立刻执行finishAnimation;- 必须确保动画器处于「暂停」或「已停止」状态时,才能调用
finishAnimation(at:); - 无论何时替换动画器,都要先处理旧动画器的状态,避免它在后台残留导致崩溃。
内容的提问来源于stack exchange,提问作者Milan Nosáľ
相关产品推荐
相关产品推荐

