UIViewPropertyAnimator切换动画中断:是否为预期行为及解决办法
UIViewPropertyAnimator 动画中断无过渡问题解答
这是预期行为吗?
是预期行为。UIViewPropertyAnimator 和 UIView.animate 的底层处理逻辑不一样:
- UIView.animate 属于隐式动画,系统会自动处理动画的中断与过渡——当新动画触发时,会基于视图当前的实时状态(比如动画执行到一半的 alpha 值)继续平滑执行新动画。
- 而你现在的写法里,每次调用 refreshView 都会创建一个全新的 UIViewPropertyAnimator 实例,这是显式动画对象。新动画启动时,旧的 animator 会被立即取消(进入
.cancelled或.current状态),视图会直接跳到目标值,自然没有过渡效果。
解决方法
核心思路是复用同一个 animator 实例,而不是每次创建新的。以下是两种可行的实现方式:
方式一:复用 animator 并基于剩余时长调整动画
var isShowingView = false var animator: UIViewPropertyAnimator? // 把 animator 改成成员变量 func onButtonClick() { isShowingView.toggle() refreshView() } func refreshView() { let targetAlpha: CGFloat = isShowingView ? 1 : 0 let timing = UICubicTimingParameters(controlPoint1: CGPoint(x: 0.4, y: 0.0), controlPoint2: CGPoint(x: 0.2, y: 1.0)) // 无正在执行的动画,直接创建新动画 guard let existingAnimator = animator, existingAnimator.isRunning else { animator = UIViewPropertyAnimator(duration: 0.3, timingParameters: timing) animator?.addAnimations { self.myView.alpha = targetAlpha } animator?.addCompletion { [weak self] _ in self?.animator = nil // 动画结束后清空,避免无用持有 } animator?.startAnimation() return } // 有正在执行的动画,基于当前状态平滑过渡到新目标 existingAnimator.pauseAnimation() let remainingDuration = existingAnimator.duration * existingAnimator.fractionComplete // 从当前 alpha 值动画到新目标 animator = UIViewPropertyAnimator(duration: remainingDuration, timingParameters: timing) animator?.addAnimations { self.myView.alpha = targetAlpha } animator?.addCompletion { [weak self] _ in self?.animator = nil } animator?.startAnimation() }
方式二:用 continueAnimation 简化过渡逻辑
这种方式直接调整正在执行的动画,代码更简洁:
var isShowingView = false var animator: UIViewPropertyAnimator? func onButtonClick() { isShowingView.toggle() refreshView() } func refreshView() { let targetAlpha: CGFloat = isShowingView ? 1 : 0 let timing = UICubicTimingParameters(controlPoint1: CGPoint(x: 0.4, y: 0.0), controlPoint2: CGPoint(x: 0.2, y: 1.0)) if let existingAnimator = animator, existingAnimator.isRunning { // 停止当前动画但保留视图的当前状态 existingAnimator.stopAnimation(false) existingAnimator.finishAnimation(at: .current) // 基于当前状态启动新动画到目标值 animator = UIViewPropertyAnimator(duration: 0.3, timingParameters: timing) animator?.addAnimations { self.myView.alpha = targetAlpha } animator?.startAnimation() } else { // 无正在执行的动画,直接创建并启动 animator = UIViewPropertyAnimator(duration: 0.3, timingParameters: timing) animator?.addAnimations { self.myView.alpha = targetAlpha } animator?.addCompletion { [weak self] _ in self?.animator = nil } animator?.startAnimation() } }
关键总结
- UIView.animate 之所以能平滑过渡,是因为系统在底层帮你封装了 animator 的复用与状态衔接逻辑。
- 直接每次创建新的 UIViewPropertyAnimator 会导致旧动画被强制中断,视图直接跳变。
- 复用 animator 并基于视图当前状态调整动画目标,才能实现和 UIView.animate 一致的平滑过渡效果。
内容的提问来源于stack exchange,提问作者Xys
相关产品推荐
相关产品推荐

