You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何观察UIViewPropertyAnimator的isRunning属性无效?该如何处理?

解决UIViewPropertyAnimator在pausesOnCompletion=true时无法检测动画结束的问题

碰到过完全一样的坑!给你理清楚整个问题的来龙去脉和解决方案:

首先,正如苹果官方文档所说,当你把UIViewPropertyAnimator的pausesOnCompletion属性设为true时,动画的完成回调(比如通过addCompletion设置的闭包)完全不会被触发——因为这个属性会让动画在结束后停留在完成状态,而非真正"完成"到触发回调的阶段。

官方一开始给出的建议是通过观察isRunning属性来判断动画是否结束,但实际这么做你会发现完全无效,比如你写了这样的代码:

animator.addObserver(self, forKeyPath: "isRunning", options: [.new], context: nil)

根本收不到任何属性变化的通知,完全没法判断动画状态。

直到我看完WWDC 2017的Session 230《Advanced Animations with UIKit》才搞明白:正确的做法是观察running属性(不带is前缀),而不是isRunning。

正确的KVO监听示例

// 定义一个上下文标记,避免KVO冲突
private var AnimationObserverContext = 0

// 添加监听
animator.addObserver(self, forKeyPath: "running", options: [.new, .initial], context: &AnimationObserverContext)

// 实现监听回调
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    guard context == &AnimationObserverContext else {
        super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
        return
    }
    
    if keyPath == "running", let isRunning = change?[.newKey] as? Bool {
        if !isRunning {
            // 这里就是动画结束的时机,pausesOnCompletion=true时会走到这里
            print("动画已结束")
        }
    }
}

// 记得在合适的时机移除监听
deinit {
    animator.removeObserver(self, forKeyPath: "running", context: &AnimationObserverContext)
}

为什么isRunning不行?

因为isRunning是UIViewPropertyAnimator的计算属性,它是基于内部的running状态和其他运行时因素来返回值的,KVO监听计算属性往往无法准确捕捉到状态变化;而running是它内部维护的真实状态属性,直接监听这个才能准确捕捉到动画从运行到停止的状态切换。

内容的提问来源于stack exchange,提问作者engili

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 08:20:20