【问题标题】:How to make my timer more precise in SwiftUI?如何在 SwiftUI 中让我的计时器更精确?
【发布时间】:2021-08-25 16:45:03
【问题描述】:

我想开发一个包含计时器的应用程序 - 到目前为止一切正常 - 但我遇到的问题是,当计数器为时,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
    }
}

【问题讨论】:

  • 这不仅仅是计时器问题,我看到多个编码问题,我在完成后发布我的答案!一个问题!它应该如何工作这个视图?你给了一个时间,它必须归零?就这个?
  • 您是否检查了 timerForCircle 的更合理持续时间,例如 1 秒?具有 0.001 或任何小于屏幕同步持续时间 10 倍的值,对动画来说毫无意义。另请参阅CADisplayLinkOptimize for variable refresh rate displays
  • @swiftPunk 是的,计时器应该做的唯一事情

标签: swift swiftui timer countdowntimer


【解决方案1】:

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

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

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

【讨论】:

  • 谢谢,现在它按照我想要的方式工作 - 现在更精确了
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多