NSTimer 有两种常用创建方式。
//第一种,使用 timerWithTimeInterval:target:selector:userInfo:repeats: 方法
timer = [NSTimer timerWithTimeInterval:1
target:singleTimerManager
selector:@selector(timerRepeat:)
userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer
forMode:NSRunLoopCommonModes];
//第二种,使用 scheduledTimerWithTimeInterval:target:selector:userInfo:repeats: 方法
timer = [NSTimer scheduledTimerWithTimeInterval:1.
target:singleTimerManager
selector:@selector(timerRepeat:)
userInfo:nil
repeats:YES];
由于第二种scheduledTimerWithTimeInterval:target:selector:userInfo:repeats: 方法是比第一种要少一行的,所以一般我会使用第二种,但今天发现,在 scrollview 加入一个 lable,lable 显示倒计时时间,那么在滑动 scrollview 时,倒计时会停止,scrollview 滑动完成以后,倒计时才会继续进行。
换成timerWithTimeInterval:target:selector:userInfo:repeats:方法以后,在滑动 scrollview 时,倒计时仍会进行,不会停止。
这两个方法的入参没什么不同,那么关键一定在[[NSRunLoop currentRunLoop] addTimer:timer
forMode:NSRunLoopCommonModes];这行,跳转 NSRunLoopCommonModes 的定义,发现还有一个 NSDefaultRunLoopMode。
FOUNDATION_EXPORT NSString * const NSDefaultRunLoopMode;
FOUNDATION_EXPORT NSString * const NSRunLoopCommonModes NS_AVAILABLE(10_5, 2_0);
将第一种方式中的 NSRunLoopCommonModes 改为 NSDefaultRunLoopMode,运行,发现效果就与第二种方式相同了。推断第二种方式是默认使用 NSDefaultRunLoopMode。
但是 NSRunLoopCommonModes 和 NSDefaultRunLoopMode 是干什么用的呢?查资料得:
- NSDefaultRunLoopMode:默认的运行模式,用于大部分操作,除了NSConnection对象事件。
- NSConnectionReplyMode:用来监控NSConnection对象的回复的,很少能够用到。
- NSModalPanelRunLoopMode:用于标明和Mode Panel相关的事件。
- NSEventTrackingRunLoopMode:用于跟踪触摸事件触发的模式(例如UIScrollView上下滚动)。
- NSRunLoopCommonModes:是一个模式集合,当绑定一个事件源到这个模式集合的时候就相当于绑定到了集合内的每一个模式。
Cocoa应用默认包含Default、Panel、Event Tracking模式,Core Foundation只包含Default模式,我们可以通过CFRunLoopAddCommonMode添加模式。
这一段我没怎么看懂,又发现了一篇文章,里面写到:
NSDefaultRunLoopModel:监听用户最基本的操作(点击,触摸等)
NSRunLoopCommonModels:监听一些特殊操作:滚动等
那么,为什么在NSDefaultRunLoopModel模式下发生滚动,计时器会停止呢?那是因为系统认为,用户不应该边滚动边操作界面,所以停止了(触碰,点击等)NSDefaultRunLoopModel模式下监听的事件。
有点儿明白了,继续查资料,在介绍 RunLoop 的文里写到:
主线程的 RunLoop 里有两个预置的 Mode:kCFRunLoopDefaultMode 和 UITrackingRunLoopMode。这两个 Mode 都已经被标记为”Common”属性。DefaultMode 是 App 平时所处的状态,TrackingRunLoopMode 是追踪 ScrollView 滑动时的状态。当你创建一个 Timer 并加到 DefaultMode 时,Timer 会得到重复回调,但此时滑动一个TableView时,RunLoop 会将 mode 切换为 TrackingRunLoopMode,这时 Timer 就不会被回调,并且也不会影响到滑动操作。
再尝试一下,改为
timer = [NSTimer timerWithTimeInterval:1 target:singleTimerManager selector:@selector(timerRepeat:) userInfo:nil repeats:YES];
//需#import <UIKit/UIApplication.h>
[[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];
则只 timer 会在 scrollview 滑动时调用 timerRepeat: 方法。