如何让我的计时器在 SwiftUI 中更精确?

How to make my timer more precise in SwiftUI?

我想开发一个包含计时器的应用程序 - 到目前为止一切正常 - 但我遇到的问题是,当计数器为 0 时,CircleProgress 并不完全为 0。 (如下图所示)

长话短说 - 我的计时器不准确...我怎样才能让它变得更好?

所以这是我的代码:

这是我将绑定绑定到 ProgressCircleView 的视图:

struct TimerView: View {
    
    //every Second change Circle and Value (Circle is small because of the animation)
    let timerForText = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
    let timerForCircle = Timer.publish(every: 0.001, on: .main, in: .common).autoconnect()
    
    @State var progress : Double = 1.0
    @State var counterCircle : Double = 0.0
    @State var counterText : Int = 0
    @State var timerSeconds : Int = 60
    
    let customInverval : Int
    
    var body: some View {
        
        ZStack{
            
            ProgressCircleView(progress: self.$progress, timerSeconds: self.$timerSeconds, customInterval: customInverval)
                .padding()
                .onReceive(timerForCircle){ time in
                    
                    //if counterCircle is the same value as Interval -> break
                    if self.counterCircle == Double(customInverval){
                        self.timerForCircle.upstream.connect().cancel()
                    } else {
                        decreaseProgress()
                    }
                    
                    counterCircle += 0.001
                }
            
            VStack{
                
                Text("\(timerSeconds)")
                    .font(.system(size: 80))
                    .bold()
                    .onReceive(timerForText){time in
                        
                        //wenn counterText is the same value as Interval -> break
                        if self.counterText == customInverval{
                            self.timerForText.upstream.connect().cancel()
                        } else {
                            incrementTimer()
                            print("timerSeconds: \(self.timerSeconds)")
                        }
                        
                        counterText += 1
                    }.multilineTextAlignment(.center)
                
            }
            .accessibilityElement(children: .combine)
            
        }.padding()
        
    }
    
    func decreaseProgress() -> Void {
        let decreaseValue : Double = 1/(Double(customInverval)*1000)
        self.progress -= decreaseValue
    }
    
    func incrementTimer() -> Void {
        let decreaseValue = 1
        self.timerSeconds -= decreaseValue
    }
    
}

这是我的 CircleProgressClass:

struct ProgressCircleView: View {
    
    @Binding var progress : Double
    @Binding var timerSeconds : Int
    
    let customInterval : Int
    
    var body: some View {
        
        ZStack{
            
            Circle()
                .stroke(lineWidth: 25)
                .opacity(0.08)
                .foregroundColor(.black)
            
            Circle()
                .trim(from: 0.0, to: CGFloat(Double(min(progress, 1.0))))
                .stroke(style: StrokeStyle(lineWidth: 20.0, lineCap: .round, lineJoin: .round))
                .rotationEffect(.degrees(270.0))
                .foregroundColor(getCircleColor(timerSeconds: timerSeconds))
                .animation(.linear)
            
        }
    }
}

func getCircleColor(timerSeconds: Int) -> Color {
    
    if (timerSeconds <= 10 && timerSeconds > 3) {
        return Color.yellow
    } else if (timerSeconds <= 3){
        return Color.red
    } else {
        return Color.green
    }
}

您无法控制计时器,它永远不会完全准确。

相反,我建议您保存结束日期并据此计算进度:

struct TimerView: View {
    
    //every Second change Circle and Value (Circle is small because of the animation)
    let timerForText = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
    let timerForCircle = Timer.publish(every: 0.001, on: .main, in: .common).autoconnect()
    
    @State var progress : Double = 1.0
    @State var timerSeconds : Int = 60
    @State var endDate: Date? = nil
    
    let customInverval : Int
    
    var body: some View {
        
        ZStack{
            
            ProgressCircleView(progress: self.$progress, timerSeconds: self.$timerSeconds, customInterval: customInverval)
                .padding()
                .onReceive(timerForCircle){ _ in
                    decreaseProgress()
                }
            
            VStack{
                
                Text("\(timerSeconds)")
                    .font(.system(size: 80))
                    .bold()
                    .onReceive(timerForText){ _ in
                        incrementTimer()
                    }.multilineTextAlignment(.center)
                
            }
            .accessibilityElement(children: .combine)
            
        }.padding()
        .onAppear {
            endDate = Date(timeIntervalSinceNow: TimeInterval(customInverval))
        }
        
    }
    
    func decreaseProgress() -> Void {
        guard let endDate = endDate else { return}
        progress = max(0, endDate.timeIntervalSinceNow / TimeInterval(customInverval))
        if endDate.timeIntervalSinceNow <= 0 {
            timerForCircle.upstream.connect().cancel()
        }
    }
    
    func incrementTimer() -> Void {
        guard let endDate = endDate else { return}
        timerSeconds = max(0, Int(endDate.timeIntervalSinceNow.rounded()))
        if endDate.timeIntervalSinceNow <= 0 {
            timerForText.upstream.connect().cancel()
            print("stop")
        }
    }
}