如何在 Swift 中设置计时器?
How can I set up timer in Swift?
我有一个 pickerController 设置小时、分钟和秒,但我不知道如何设置我的计时器,这是我的代码,但它不起作用,我该如何更改它?提前致谢
func startTimer() {
if timer.isValid == true || timeHours == 0 || timeMinutes == 0 || timeSeconds == 0 {
//do nothing
} else {
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(onTimerFires), userInfo: nil, repeats: true)
}
}
@objc func onTimerFires() {
timeHours -= 1
timeMinuteLabel.text = timeFunc()
if timeSeconds == 0 {
timer.invalidate()
}
}
func timeFunc() -> String {
let hours = Int(optionalValuePickerView.selectedRow(inComponent: 0))
let minutes = Int(optionalValuePickerView.selectedRow(inComponent: 1))
let seconds = Int(optionalValuePickerView.selectedRow(inComponent: 2))
return String(format: "%02i:%02i:%02i", hours, minutes, seconds)
}
这里有几个问题:
每次调用 onTimerFires
时,它递减 timeHours
(似乎没有在任何地方设置或使用)然后调用 timeFunc
,这只是从选择器中获取值(在任何地方都不会改变)来构建字符串。
这种在计时器中递减计数器的模式做出了一个很大的假设,即计时器在正确的时间被调用并且它永远不会错过计时器调用。不幸的是,这不是一个谨慎的假设。
我建议不要使用基于 Selector
的计时器,因为它会引入 target
的强引用循环。具有 weak
引用的基于块的再现更容易避免此类循环。
我会建议一个不同的模式,即您保存倒计时的时间:
var countDownDate: Date!
func updateCountDownDate() {
let components = DateComponents(hour: pickerView.selectedRow(inComponent: 0),
minute: pickerView.selectedRow(inComponent: 1),
second: pickerView.selectedRow(inComponent: 2))
countDownDate = Calendar.current.date(byAdding: components, to: Date())
}
然后您的计时器处理程序可以计算到目标倒计时的经过时间量 date/time:
func handleTimer(_ timer: Timer) {
let remaining = countDownDate.timeIntervalSince(Date())
guard remaining >= 0 else {
timer.invalidate()
return
}
label.text = timeString(from: remaining)
}
启动计时器时,计算 countDownDate
并安排计时器:
weak var timer: Timer?
func startTimer() {
timer?.invalidate() // if one was already running, cancel it
updateCountDownDate()
timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] timer in
guard let self = self else {
timer.invalidate()
return
}
self.handleTimer(timer)
}
timer?.fire()
}
而且,值得一提的是,在准备字符串时,您当然可以确定日期之间的日期组件(即现在和您的目标 date/time),但您也可以使用 DateComponentsFormatter
为你做这个:
let formatter: DateComponentsFormatter = {
let formatter = DateComponentsFormatter()
formatter.unitsStyle = .positional
formatter.allowedUnits = [.hour, .minute, .second]
formatter.zeroFormattingBehavior = .pad
return formatter
}()
func timeString(from interval: TimeInterval) -> String? {
return formatter.string(from: interval)
}
我有一个 pickerController 设置小时、分钟和秒,但我不知道如何设置我的计时器,这是我的代码,但它不起作用,我该如何更改它?提前致谢
func startTimer() {
if timer.isValid == true || timeHours == 0 || timeMinutes == 0 || timeSeconds == 0 {
//do nothing
} else {
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(onTimerFires), userInfo: nil, repeats: true)
}
}
@objc func onTimerFires() {
timeHours -= 1
timeMinuteLabel.text = timeFunc()
if timeSeconds == 0 {
timer.invalidate()
}
}
func timeFunc() -> String {
let hours = Int(optionalValuePickerView.selectedRow(inComponent: 0))
let minutes = Int(optionalValuePickerView.selectedRow(inComponent: 1))
let seconds = Int(optionalValuePickerView.selectedRow(inComponent: 2))
return String(format: "%02i:%02i:%02i", hours, minutes, seconds)
}
这里有几个问题:
每次调用
onTimerFires
时,它递减timeHours
(似乎没有在任何地方设置或使用)然后调用timeFunc
,这只是从选择器中获取值(在任何地方都不会改变)来构建字符串。这种在计时器中递减计数器的模式做出了一个很大的假设,即计时器在正确的时间被调用并且它永远不会错过计时器调用。不幸的是,这不是一个谨慎的假设。
我建议不要使用基于
Selector
的计时器,因为它会引入target
的强引用循环。具有weak
引用的基于块的再现更容易避免此类循环。
我会建议一个不同的模式,即您保存倒计时的时间:
var countDownDate: Date!
func updateCountDownDate() {
let components = DateComponents(hour: pickerView.selectedRow(inComponent: 0),
minute: pickerView.selectedRow(inComponent: 1),
second: pickerView.selectedRow(inComponent: 2))
countDownDate = Calendar.current.date(byAdding: components, to: Date())
}
然后您的计时器处理程序可以计算到目标倒计时的经过时间量 date/time:
func handleTimer(_ timer: Timer) {
let remaining = countDownDate.timeIntervalSince(Date())
guard remaining >= 0 else {
timer.invalidate()
return
}
label.text = timeString(from: remaining)
}
启动计时器时,计算 countDownDate
并安排计时器:
weak var timer: Timer?
func startTimer() {
timer?.invalidate() // if one was already running, cancel it
updateCountDownDate()
timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] timer in
guard let self = self else {
timer.invalidate()
return
}
self.handleTimer(timer)
}
timer?.fire()
}
而且,值得一提的是,在准备字符串时,您当然可以确定日期之间的日期组件(即现在和您的目标 date/time),但您也可以使用 DateComponentsFormatter
为你做这个:
let formatter: DateComponentsFormatter = {
let formatter = DateComponentsFormatter()
formatter.unitsStyle = .positional
formatter.allowedUnits = [.hour, .minute, .second]
formatter.zeroFormattingBehavior = .pad
return formatter
}()
func timeString(from interval: TimeInterval) -> String? {
return formatter.string(from: interval)
}