如何在Swift中通过编程模拟惯性滚动?
实现带惯性的模拟滚动事件
要实现和触控板一致的惯性滚动效果,核心是记录手势结束时的速度,然后通过逐帧发送衰减的滚动事件来模拟减速过程,具体步骤如下:
1. 记录手势结束时的滚动速度
在手势识别回调(比如UIPanGestureRecognizer的处理方法)中,捕获手势结束瞬间的速度值,以此作为惯性滚动的初始速度:
// 类级变量存储速度与定时器 private var scrollVelocity: CGFloat = 0.0 private var displayLink: CADisplayLink? // 手势处理示例 @objc func handlePanGesture(_ gesture: UIPanGestureRecognizer) { switch gesture.state { case .changed: // 原有实时滚动逻辑 let delta = gesture.translation(in: view).y sendScrollEvent(delta: delta) gesture.setTranslation(.zero, in: view) case .ended, .cancelled: // 记录手势结束时的y轴速度 scrollVelocity = gesture.velocity(in: view).y // 启动惯性滚动 startInertialScroll() default: break } }
2. 实现惯性滚动的衰减逻辑
用CADisplayLink(和屏幕刷新率同步,保证流畅性)逐帧生成衰减的滚动事件,模拟自然减速:
private func startInertialScroll() { // 先停止之前的定时器,避免重复触发 displayLink?.invalidate() displayLink = CADisplayLink(target: self, selector: #selector(updateInertialScroll)) displayLink?.add(to: .main, forMode: .common) } @objc private func updateInertialScroll() { // 衰减系数:0.9左右接近系统触控板手感,值越小减速越快 let dampingFactor: CGFloat = 0.9 // 最小速度阈值:低于此值停止惯性滚动 let minVelocityThreshold: CGFloat = 1.0 // 更新速度 scrollVelocity *= dampingFactor // 计算本次滚动的delta值,微调0.1系数控制每帧滚动幅度 let delta = scrollVelocity * 0.1 // 发送滚动事件 sendScrollEvent(delta: delta) // 速度低于阈值时停止惯性滚动 if abs(scrollVelocity) < minVelocityThreshold { displayLink?.invalidate() displayLink = nil scrollVelocity = 0.0 } } // 封装原有滚动事件发送逻辑 private func sendScrollEvent(delta: CGFloat) { let event = CGEvent( scrollWheelEvent2Source: nil, units: .pixel, wheelCount: 1, wheel1: delta, wheel2: 0, wheel3: 0 ) guard let scrollEvent = event else { return } scrollEvent.post(tap: .cghidEventTap) }
3. 调优细节
- 衰减系数:可在0.85-0.95区间调整
dampingFactor,匹配你想要的惯性手感。 - 方向适配:如果是横向滚动,只需将y轴速度替换为x轴速度即可。
- 性能控制:确保
CADisplayLink在惯性结束后及时销毁,避免不必要的性能消耗。
内容的提问来源于stack exchange,提问作者emvaized
相关产品推荐
相关产品推荐

