SwiftUI - 如何制作 Start/Stop 计时器

SwiftUI - How to Make A Start/Stop Timer

我的目标是在 SwiftUI 中创建一个以 0 开头的视图。当您按下视图时,计时器应该开始向上计数,再次点击会停止计时器。最后,当您再次点击以启动计时器时,计时器应从 0 开始。

这是我当前的代码:

import SwiftUI

struct TimerView: View {
    @State var isTimerRunning = false
    @State private var endTime = Date()
    @State private var startTime =  Date()
    let timer = Timer.publish(every: 0.001, on: .main, in: .common).autoconnect()
    
    var tap: some Gesture {
        TapGesture(count: 1)
            .onEnded({
                isTimerRunning.toggle()
            })
    }

    var body: some View {

        Text("\(endTime.timeIntervalSince1970 - startTime.timeIntervalSince1970)")
            .font(.largeTitle)
            .gesture(tap)
            .onReceive(timer) { input in
                startTime = isTimerRunning ? startTime : Date()
                endTime = isTimerRunning ? input : endTime
            }

    }
}

此代码会导致计时器立即启动并且永不停止,即使我点击它也是如此。计时器也会倒退(变成负数)而不是前进。

有人可以帮我理解我做错了什么吗?另外,我想知道这是否是一个很好的计时器整体策略(使用 Timer.publish)。

谢谢!

这里是固定版本。看看我所做的更改。

    如果计时器为 运行,
  • .onReceive 现在会更新 timerString。 timeString 是现在(即 Date())和 startTime.
  • 之间的间隔
  • 点击计时器设置 startTime 如果不是 运行。

struct TimerView: View {
    @State var isTimerRunning = false
    @State private var startTime =  Date()
    @State private var timerString = "0.00"
    let timer = Timer.publish(every: 0.01, on: .main, in: .common).autoconnect()

    var body: some View {

        Text(self.timerString)
            .font(Font.system(.largeTitle, design: .monospaced))
            .onReceive(timer) { _ in
                if self.isTimerRunning {
                    timerString = String(format: "%.2f", (Date().timeIntervalSince( self.startTime)))
                }
            }
            .onTapGesture {
                if !isTimerRunning {
                    timerString = "0.00"
                    startTime = Date()
                }
                isTimerRunning.toggle()
            }
    }
}

上面的版本虽然简单,但让我感到困扰的是 Timer 一直在发布。我们只需要在计时器为 运行.

时发布 Timer

这是启动和停止 Timer:

的版本
struct TimerView: View {
    @State var isTimerRunning = false
    @State private var startTime =  Date()
    @State private var timerString = "0.00"
    @State private var timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()

    var body: some View {

        Text(self.timerString)
            .font(Font.system(.largeTitle, design: .monospaced))
            .onReceive(timer) { _ in
                if self.isTimerRunning {
                    timerString = String(format: "%.2f", (Date().timeIntervalSince( self.startTime)))
                }
            }
            .onTapGesture {
                if isTimerRunning {
                    // stop UI updates
                    self.stopTimer()
                } else {
                    timerString = "0.00"
                    startTime = Date()
                    // start UI updates
                    self.startTimer()
                }
                isTimerRunning.toggle()
            }
            .onAppear() {
                // no need for UI updates at startup
                self.stopTimer()
            }
    }
    
    func stopTimer() {
        self.timer.upstream.connect().cancel()
    }
    
    func startTimer() {
        self.timer = Timer.publish(every: 0.01, on: .main, in: .common).autoconnect()
    }
}