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