如上,在UIScrollView上添加一个UILabel, UILabel的text通过添加一个Timer来每一秒更新一次
func createTimer() {
timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true, block: { _ in
self.timerLabel.text = "(self.count + 1)"
self.count += 1
})
}
如上为创建Timer代码
从上面gif中可以看出:当滑动屏幕时UILabel的text停止更新了,也就是Timer的回调未被执行,当手离开屏幕停止滑动时,UILabel的text又开始更新了,也就是Timer的回调又被执行了。
这是为什么呢?
当我第一次遇到该类问题时,真是摸不着头脑,算是一个知识盲区
后来上网搜了一下,又学了一下RunLoop,算是明白了,其实这就是一个RunLoop的mode切换问题
必要的知识点:
RunLoop只能同时在一种mode下工作
RunLoop常用的三种mode:
kCFRunLoopDefaultMode : App的默认mode,通常主线程是在该mode下运行的
UITrackingRunLoopMode : 界面跟踪mode, 用于ScrollView追踪触摸滑动,保证界面滑动时不受其他mode影响
kCFRunLoopCommonModes
如上代码注册的Timer会被添加到当前RunLoop的kCFRunLoopDefaultMode,但当滑动时,mode会被切换到UITrackingRunLoopMode, 注册在kCFRunLoopDefaultMode的Timer将不会被执行,当停止滑动时又会切换回kCFRunLoopDefaultMode,Timer将会继续被执行。
所以如果要在滑动时Timer继续执行的话,解决的方法其实很简单。
可以把Timer添加到这两个mode中:kCFRunLoopDefaultMode,UITrackingRunLoopMode
let runLoop = RunLoop.current
runLoop.add(timer, forMode: .default)
runLoop.add(timer, forMode: .tracking)
或者把该Timer添加到kCFRunLoopCommonModes,这样上面两个mode都可以执行这个Timer
let runLoop = RunLoop.current
runLoop.add(timer, forMode: .common)
最后运行效果
\