Timer使用指南
Swift的Timer类(前身是NSTimer)是一种灵活规划未来预定事件的方法,可以仅触发一次或不断循环。在这篇指南中我会提供多种使用它的方式,并带有一些常见问题的解决办法。
注意:
我要首先声明,使用timers会有很大的电力消耗。我们会想办法减少它,但任何类型的timers要想触发,都要从静止状态下唤醒系统,并会有相应的消耗。
创建一个循环timer
从最基础的开始,创建并启用一个循环timer来调用一个方法:
let
timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector:#selector(fireTimer), userInfo: nil, repeats: true)
这里我们为了测试用了fireTimer()方法:
@objc
func
fireTimer
()
{"Timer fired!"
)}
尽管我们要求timer每隔1.0秒触发一次,iOS会让timer稍微有点宽容度——可能你的timer很难精确的间隔1.0秒触发。
另一个创建循环timer的常用方法是使用闭包:
let
timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats:true
) { timerin
"Timer fired!"
)}
这些初始化都可以创建timer,不需要把它存在某个属性中,但那样做会比较好,能够方便晚些终止这个timer。因为闭包方法每次代码运行时都要通过timer,你也可以从这方面终止它。
创建一个非循环timer
如果你想代码只运行一次,就把repeats: true改成repeats: false
let
timer1 = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector:#selector(fireTimer), userInfo: nil, repeats: false)
let
timer2 = Timer.scheduledTimer(withTimeInterval: 1.0, repeats:false
) { timerin
"Timer fired!"
)}
其他代码不变
尽管这种方法听上去很完美,我个人还是推荐用GCD来实现
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
"Timer fired!"
)}
结束一个timer
你可以调用invalidate()方法来销毁已存在的timer。
例如下面代码创建每秒打印“Timer fired!”1次,共打印3次的timer,之后终止它。
var
runCount =0
Timer.scheduledTimer(withTimeInterval:
1.0
, repeats:true
) {timer
in
"Timer fired!"
)runCount +=
1
if
runCount ==3
{timer.invalidate()
}
}
如果通过一个方法结束一个timer,首先需要声明一个timer和一个runCount属性
var
timer: Timer?var
runCount =0
之后规划好timer
timer
= Timer.scheduledTimer(timeInterval:1.0
, target: self, selector: #selector(fireTimer), userInfo: nil, repeats:true
)最后,填写fireTimer()方法
@objc
func
fireTimer
()
{"Timer fired!"
)runCount +=
1
if
runCount ==3
{timer?.invalidate()
}
}
另一种方法是,让fireTimer()接收timer作为其参数,这样就不需要使用timer属性。需要这样重写fireTimer()
@objc
func
fireTimer
(timer: Timer)
{"Timer fired!"
)runCount +=
1
if
runCount ==3
{timer.invalidate()
}
}
附加context
当你创建timer来执行一个方法时,你可以附加一些context,用于存储额外的timer触发条件信息。它是一个字典,可以存任意量的数据——比如触发timer的事件,用户在做些什么,哪个table view被选中等等
比如我们可以让这个字典包含有一个用户名:
let
context = ["user"
:"
@twostraws
"]Timer.scheduledTimer(timeInterval:
1
.0
, target: self, selector:#selector(fireTimer), userInfo: context, repeats: true)
我们之后可以通过查看timer 参数的userInfo属性来读取fireTimer()
@
objc func
fireTimer
(timer: Timer
) {guard
let
context = timer.userInfoas
? [String: String]else
{return
}let
user = context["user"
,default
:"Anonymous"
]print(
"Timer fired by \(user)!"
)runCount +=
1
if
runCount ==3
{timer.invalidate()
}
}
添加一些时间宽容度(tolerance)
给你的timer添加一些时间宽容度可以降低它的电力消耗。它允许你给系统留一些timer执行时间的冗余。“我希望1秒钟运行一次,但是晚个200毫秒我也不介意”。这允许系统协同运行多个timer,把多个timer事件合并到一起,节省电池寿命。
当你指定了时间宽容度,就意味着系统可以在原有时间附加该宽容度内的任意时刻触发timer。例如,如果你要timer 1秒后运行,并有0.5秒的时间宽容度,实际就可能是1秒,1.5秒或1.3秒等。
下例中创建了一个1秒运行一次的timer,并有0.2秒的时间宽容度:
let
timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector:#selector(fireTimer), userInfo: nil, repeats: true)
timer.tolerance = 0.2
默认的时间宽容度是0,但是系统会自动添加一个很小的宽容度。
如果一个重复性timer由于设定的时间宽容度推迟了一小会执行,这并不意味着后续的执行都会晚一会。iOS不允许timer总体上的漂移,也就是说下一次触发会快一些。
举例的话,如果一个timer每1秒运行一次,并有0.5秒的时间宽容度,那么实际可能是这样:
1.0秒后timer触发
2.4秒后timer再次触发,晚了0.4秒,但是在时间宽容度内
3.1秒后timer第三次触发,和上一次仅差0.7秒,但每次触发的时间是按原始时间算的。
等等…
与runloops协同使用
在app中实际使用中,人们经常会遇到timer并没有触发的情况。比如用户用手指触摸屏幕,滚动一个table view的时候,即使设定好条件timer也不会触发。
这是由于我们默认把timer创建为defaultRunLoopMode,这是我们app的主线程。所以当用户与UI正在互动时会暂停,当用户停下后才再次触发。
最简单的解决办法是在创建timer时不直接规划它,而是手动把它添加到一个runloop中。本例中,我们选用了.commonModes:即使UI正在使用,它也允许timer触发。
let
context = ["user"
:"@twostraws"
]let
timer = Timer(timeInterval:1.0
, target: self, selector:#selector(fireTimer), userInfo: context, repeats: true)
RunLoop.current.
add
(timer, forMode: .commonModes)把timer与屏幕刷新同步
一些人,尤其是游戏开发者,会尝试在每帧被绘制之前让timer完成一些工作。
但这是错误的:timer并不具备这么高的精确度,人们也无法知道上一帧被绘制后过去了多少时间。你也许会设置每秒运行60或120次代码,但实际上在你的timer触发之前可能半数都被跳过了。
所以如果你想要一些代码在屏幕刷新后立即运行,你要使用CADisplayLink。下面是一些关于CADisplayLink的代码
let
displayLink = CADisplayLink(target: self, selector:#selector(fireTimer))
displayLink.
add
(to: .current, forMode: .defaultRunLoopMode)别忘了,如果你想要DisplayLink方法在UI被使用时也能触发,请指定.commonModes,而不是用.defaultRunLoopMode。
相关推荐:
Runtime的应用
iOS底层原理总结 - Category的本质
WKWebView的15条应用指南
- "职场塑料五件套"表情包火了!送你详细使用指南
- 12星座的恋爱小气症,12星座男使用指南,吵架王排行
- Taylor Swift出售两套豪宅,对比后觉得深圳“五千万蹲”太不值了
- 逐梦之路H5使用指南出炉啦!
- 发福后的Taylor Swift这次瘦了,但肌肉腿有点触目惊心!
- 全球无障碍宣传日,我们准备了这份特别的 iPhone 使用指南 | 有
- 可以抛弃 Python 了?Google 开源 Swift for TensorFlow
- 五一送爸妈最好的礼物!超赞手机使用指南
- 弘瑞3D打印切片及控制系统使用指南(下篇)
- 这是一份“咨询顾问”使用指南,请查收【宁宇专栏-249】