如何在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())
}

然后,您的计时器处理程序可以计算到目标倒计时日期/时间的经过时间:

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()
}

而且,值得一提的是,在准备字符串时,您当然可以确定到日期之间的日期成分(即现在和您的目标日期/时间(,但您也可以使用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)
}

最新更新