Swift中ScrollView执行UIView动画时如何获取实时contentOffset偏移量
实现方案
UIKit 动画过程中修改的是图层的呈现层(presentation layer)属性,直接读取 scrollView.contentOffset 只能拿到动画的最终目标值,要获取实时偏移量可以用以下方案:
方案1:CADisplayLink 实时监听(全系统版本兼容)
该方案和屏幕刷新率保持同步,取值精度最高,是最常用的实现方式:
- 首先声明全局的
CADisplayLink属性:
private var displayLink: CADisplayLink?
- 在启动 ScrollView 动画的同时初始化监听:
// 原有的ScrollView动画代码 UIView.animate(withDuration: Double(totalMidiTime) / 1000, delay: 0, options: .curveLinear) { self.scrollView.contentOffset.x = self.scrollView.contentSize.width - self.leftMargin } completion: { [weak self] (_) in // 动画结束后销毁监听,避免内存泄漏 self?.displayLink?.invalidate() self?.displayLink = nil } // 启动实时监听 displayLink = CADisplayLink(target: self, selector: #selector(getRealtimeOffset)) displayLink?.add(to: .main, forMode: .common)
- 实现监听方法,从呈现层读取实时偏移量:
@objc private func getRealtimeOffset() { guard let presentationLayer = scrollView.layer.presentation() else { return } // presentationLayer.bounds.origin.x 就等于当前实时的contentOffset.x let realtimeOffsetX = presentationLayer.bounds.origin.x // 此处可添加你需要的业务逻辑,比如打印、联动其他视图等 print("实时X偏移量:\(realtimeOffsetX)") }
方案2:iOS 15+ 动画进度回调
如果最低兼容版本为iOS 15及以上,可以使用系统提供的动画进度回调API,不过该方案使用场景局限性更大,更推荐优先使用方案1。
注意事项
动画执行结束后必须调用
displayLink.invalidate()销毁监听对象,否则会造成内存泄漏
不要直接读取scrollView.contentOffset作为动画过程中的实时值,该属性在动画触发时就已经被赋值为最终的目标值scrollView.layer.presentation()返回的是图层当前屏幕上正在渲染的实时状态,所有动画过程中的实时值都要从该对象读取
内容的提问来源于stack exchange,提问作者Norman
相关产品推荐
相关产品推荐

