添加到NSRunLoop的timer在滑动时失效的解决

481 阅读1分钟

在阅读文章《深入理解RunLoop》的时候看到个某次笔试时的问题,当时也没写出来。毕竟以前也没碰到这种情况,哎。

在将Timer添加到currentRunLoop的时候,如果当前界面中出现一个ScrollView在滑动的话,此时Timer就不会进行回调。

从文章上看到:

主线程的 RunLoop 里有两个预置的 Mode:kCFRunLoopDefaultMode 和 UITrackingRunLoopMode。这两个 Mode 都已经被标记为"Common"属性。DefaultMode 是 App 平时所处的状态,TrackingRunLoopMode 是追踪 ScrollView 滑动时的状态。当你创建一个 Timer 并加到 DefaultMode 时,Timer 会得到重复回调,但此时滑动一个TableView时,RunLoop 会将 mode 切换为 TrackingRunLoopMode,这时 Timer 就不会被回调,并且也不会影响到滑动操作

在scroll view滑动的时候,RunLoop会切换到UITrackingRunLoopMode这个模式。原本的Default Mode就不会执行了。

解决方法:

在两个 Mode 中都能得到回调,一种办法就是将这个 Timer 分别加入这两个 Mode。还有一种方式,就是将 Timer 加入到顶层的 RunLoop 的 "commonModeItems" 中。"commonModeItems" 被 RunLoop 自动更新到所有具有"Common"属性的 Mode 里去。

@implementation ViewController {
    int _num;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    _num = 0;
	NSTimer *timer = [NSTimer timerWithTimeInterval:1.0
                                             target:self
                                           selector:NSSelectorFromString(@"test")
                                           userInfo:nil
                                            repeats:YES];
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];
//    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

    UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:self.view.bounds];
    scrollView.contentSize = CGSizeMake(self.view.bounds.size.width, self.view.bounds.size.height * 5);
    for (int i = 0; i < 100; i++) {
        UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 20 * i, self.view.bounds.size.width, 20)];
        CGFloat r = arc4random_uniform(255) / 255.0;
        CGFloat g = arc4random_uniform(255) / 255.0;
        CGFloat b = arc4random_uniform(255) / 255.0;
        view.backgroundColor = [UIColor colorWithRed:r green:g blue:b alpha:1];
        [scrollView addSubview:view];
    }
    [self.view addSubview:scrollView];

}

- (void)test {
    NSLog(@"%d", _num++);
}


- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}
@end