在 iOS 开发中,使用 +scheduledTimerWithTimeInterval:target:selector:userInfo:repeats: 创建的定时器(NSTimer)在滑动页面上的列表时可能会出现回调暂停的现象。这主要是因为定时器的运行依赖于 RunLoop 的模式,而在滑动列表时,RunLoop 会进入一个特殊的模式(如 UITrackingRunLoopMode),这会导致定时器的回调被暂停。
原因分析
RunLoop 模式:
当用户在滑动列表时,RunLoop 会切换到 UITrackingRunLoopMode 模式。在这个模式下,只有与用户交互相关的事件会被处理,定时器通常会被暂停,因为它们是在 NSDefaultRunLoopMode 中运行的。
定时器的添加模式:
使用 +scheduledTimerWithTimeInterval: 方法创建的定时器默认添加到 NSDefaultRunLoopMode,这意味着当 RunLoop 切换到 UITrackingRunLoopMode 时,定时器的回调不会被触发。
解决方案
要解决这个问题,可以将定时器添加到 NSRunLoopCommonModes 模式,这样它就可以在多个模式下运行,包括滑动时的 UITrackingRunLoopMode。下面是如何实现这一点的示例代码:
// 创建并添加定时器到 NSRunLoopCommonModes NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(handleTimer) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
其他注意事项
手动管理定时器:
如果你需要在不同的模式下手动管理定时器,可以在滑动开始和结束时手动添加或移除定时器。
使用 GCD 或 CADisplayLink:
如果你发现 NSTimer 的行为不符合需求,可以考虑使用 Grand Central Dispatch (GCD) 或 CADisplayLink。CADisplayLink 是一个专门用于与屏幕刷新同步的定时器,适合处理动画和高频率的更新。
示例代码:使用 CADisplayLink
CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(handleDisplayLink)];
[displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
在滑动列表时,NSTimer 的回调暂停是由于 RunLoop 模式的切换导致的。通过将定时器添加到 NSRunLoopCommonModes,可以确保它在滑动时继续工作。此外,考虑使用 CADisplayLink 或 GCD 作为替代方案,以满足更高频率的更新需求。