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

滑动UIScrollView时内部绑定计时器的视图动画停止运行如何解决

问题根本原因

这个问题和视图层级没有关系,是iOS的RunLoop运行机制导致的:

  • 默认创建的Timer默认被添加到主线程RunLoop的default模式下,只有RunLoop处于default模式时Timer才会触发
  • 当滑动UIScrollView时,主线程RunLoop会自动切换到UITrackingRunLoopMode(专门用来处理滑动交互,保证滑动流畅),此时default模式下的所有事件源(包括你创建的Timer)都会被暂时暂停,直到滑动结束RunLoop切回default模式才会恢复。
可行解决方案

方案1:修改Timer的RunLoop添加模式

把Timer添加到RunLoop的common模式下,common模式是模式集合的标记,会同时在default和UITrackingRunLoopMode下生效,滑动时也能正常触发Timer。
代码示例:

// 不要直接用scheduledTimer自动添加,先创建Timer实例
let timer = Timer(timeInterval: 1, target: self, selector: #selector(lowBeatingAnimation), userInfo: nil, repeats: true)
// 手动添加到主线程RunLoop的common模式
RunLoop.main.add(timer, forMode: .common)

iOS10以上的block写法Timer逻辑一致,先创建实例再手动添加到对应RunLoop模式即可。


方案2:使用GCD定时器(更推荐)

GCD的定时器不依赖RunLoop,完全不受RunLoop模式切换的影响,精度也更高,是更稳定的方案。
代码示例:

// 需在当前类声明GCD定时器属性,持有对象避免被释放
var gcdTimer: DispatchSourceTimer?

// 初始化定时器
gcdTimer = DispatchSource.makeTimerSource(queue: .main)
// 设置每秒触发一次,允许的误差0.1秒(可根据业务需求调整)
gcdTimer?.schedule(deadline: .now(), repeating: 1, leeway: .milliseconds(100))
// 设置触发回调
gcdTimer?.setEventHandler { [weak self] in
    self?.lowBeatingAnimation()
}
// 启动定时器
gcdTimer?.resume()

// 不需要时记得销毁定时器避免内存泄漏
// gcdTimer?.cancel()
// gcdTimer = nil

方案3:补充动画配置避免滑动时动画中断

即使Timer能正常触发,滑动时默认的动画也可能被交互打断,可以给你的动画添加两个配置参数,保证滑动时动画正常执行:
把你的动画代码的options参数做如下修改:

  • 原.curveEaseIn改为[.curveEaseIn, .allowUserInteraction, .beginFromCurrentState]
  • 原.curveEaseOut改为[.curveEaseOut, .allowUserInteraction, .beginFromCurrentState]

参数说明:

  • .allowUserInteraction:允许动画执行期间响应交互
  • .beginFromCurrentState:如果动画被打断,从当前状态继续执行新的动画,不会出现视觉跳变

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 19:15:03