繁体   English   中英

SwiftUI 计时器在更新 ObservedObjects 时不断重置

[英]SwiftUI Timer keep resetting when updating the ObservedObjects

我只是制作了一个简单的计时器代码,并且正在尝试找出一种方法,以便在其他观察到的 object 状态发生更改时不重置计时器。 当我使用 init function 启动计时器时,只要其他观察到的对象的状态发生变化,它就会重置。 当我用 onAppear 启动计时器时,一旦其他观察到的对象的状态发生变化,它就会发生变化,并且再也不会启动。 我想要完成的是,计时器启动一次,并且在其他观察到的对象从其他 View 传递出去并且 tiemr 本身必须是 Subview 期间其他观察到的对象发生变化时不会重置。 有什么建议么?

import SwiftUI
import Combine
import Foundation

struct ContentView: View {

    @ObservedObject var apptCardVM: ApptCardViewModel
    @ObservedObject var timerData = TimerDataViewModel()

    var body: some View {
        VStack {
            CurrentDateView(timerData: timerData)  // << here !!

            Picker("Seizure Type", selection: $apptCardVM.typeIndex) {
            ForEach(0..<apptCardVM.typeChoice.count) {
                Text(self.apptCardVM.typeChoice[$0])
            }
        }.pickerStyle(SegmentedPickerStyle())

        }

    }
}

struct CurrentDateView: View {
    @State private var currentDate = Date()
    let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
    @ObservedObject var timerData: TimerDataViewModel
    var body: some View {

        Text("\(Int(timerData.hoursElapsed), specifier: "%02d"):\(Int(timerData.minutesElapsed), specifier: "%02d"):\(Int(timerData.secondsElapsed), specifier: "%02d")")
            .fontWeight(.bold)
            .textFieldStyle(RoundedBorderTextFieldStyle())
            .onAppear(){
                timerData.start()
            }
    }
}

class ApptCardViewModel: ObservableObject, Identifiable {
    @Published var typeChoice = ["Quick", "Long", "FullService"]
    @Published var typeIndex: Int = 0
    private var cancellables = Set<AnyCancellable>()


}

class TimerDataViewModel: ObservableObject{
    
    @Published var timer = Timer()
    @Published var startTime : Double = 0.0
    @Published var secondsOriginal = 0.0
    @Published var secondsElapsed = 0.0
    @Published var secondsElapsed_ = 0.0
    @Published var minutesElapsed = 0.0
    @Published var hoursElapsed = 0.0
    
    enum stopWatchMode {
        case running
        case stopped
        case paused
    }
    
    init(){
//        start()
        print("initialized")
    }

    
    func start(){
            self.secondsOriginal = self.startTime
            self.timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true){ timer in
                self.secondsOriginal += 1
                self.secondsElapsed_ = Double(Int(self.secondsOriginal))
                self.secondsElapsed = Double(Int(self.secondsOriginal)%60)
                self.minutesElapsed = Double(Int(self.secondsOriginal)/60 % 60)
                self.hoursElapsed = Double(Int(self.secondsOriginal)/3600 % 24)
            }
    }
}
import SwiftUI
import Combine
struct SeizureView: View {
    
    
    @ObservedObject var apptCardVM: ApptCardViewModel
    //This will solve your issue.
    @StateObject var timerData = TimerDataViewModel()
    @State private var currentDate = Date()
    var body: some View {
        VStack {
            CurrentDateView(currentDate: $currentDate, timerData: timerData)  
            //But something to consider
            //This View is reusable and does not need a timer. Just a Date object
            TimerView(date: currentDate, showSubseconds: false)
            Picker("Seizure Type", selection: $apptCardVM.typeIndex) {
                ForEach(0..<apptCardVM.typeChoice.count) {
                    Text(self.apptCardVM.typeChoice[$0])
                }
            }.pickerStyle(SegmentedPickerStyle())
            
        }
        
    }
}

struct CurrentDateView: View {
    @Binding var currentDate : Date
    let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
    @ObservedObject var timerData: TimerDataViewModel
    var body: some View {
        
        Text("\(Int(timerData.hoursElapsed), specifier: "%02d"):\(Int(timerData.minutesElapsed), specifier: "%02d"):\(Int(timerData.secondsElapsed), specifier: "%02d")")
            .fontWeight(.bold)
            .textFieldStyle(RoundedBorderTextFieldStyle())
            .onAppear(){
                timerData.start()
            }
    }
}

class ApptCardViewModel: ObservableObject, Identifiable {
    @Published var typeChoice = ["Quick", "Long", "FullService"]
    @Published var typeIndex: Int = 0
    private var cancellables = Set<AnyCancellable>()
    
    
}

class TimerDataViewModel: ObservableObject{
    
    @Published var timer = Timer()
    @Published var startTime : Double = 0.0
    @Published var secondsOriginal = 0.0
    @Published var secondsElapsed = 0.0
    @Published var secondsElapsed_ = 0.0
    @Published var minutesElapsed = 0.0
    @Published var hoursElapsed = 0.0
    
    enum stopWatchMode {
        case running
        case stopped
        case paused
    }
    
    init(){
        //        start()
        print("initialized")
    }
    
    
    func start(){
        self.secondsOriginal = self.startTime
        self.timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true){ timer in
            self.secondsOriginal += 1
            self.secondsElapsed_ = Double(Int(self.secondsOriginal))
            self.secondsElapsed = Double(Int(self.secondsOriginal)%60)
            self.minutesElapsed = Double(Int(self.secondsOriginal)/60 % 60)
            self.hoursElapsed = Double(Int(self.secondsOriginal)/3600 % 24)
        }
    }
}

struct TimerView: View {
    var date: Date
    var showSubseconds: Bool
    var fontWeight: Font.Weight = .bold
    
    var body: some View {
        if #available(watchOSApplicationExtension 8.0, watchOS 8.0, iOS 15.0, *) {
            //The code from here is mostly from https://developer.apple.com/wwdc21/10009
            TimelineView(MetricsTimelineSchedule(from: date)) { context in
                ElapsedTimeView(elapsedTime: -date.timeIntervalSinceNow, showSubseconds: showSubseconds)
            }
        } else {
            Text(date,style: .timer)
                .fontWeight(fontWeight)
                .clipped()
        }
    }
}
@available(watchOSApplicationExtension 8.0, watchOS 8.0, iOS 15.0,*)
private struct MetricsTimelineSchedule: TimelineSchedule {
    var startDate: Date
    
    init(from startDate: Date) {
        self.startDate = startDate
        
    }
    
    func entries(from startDate: Date, mode: TimelineScheduleMode) -> PeriodicTimelineSchedule.Entries {
        PeriodicTimelineSchedule(from: self.startDate, by: (mode == .lowFrequency ? 1.0 : 1.0 / 30.0))
            .entries(from: startDate, mode: mode)
    }
}
struct ElapsedTimeView: View {
    var elapsedTime: TimeInterval = 0
    var showSubseconds: Bool = false
    var fontWeight: Font.Weight = .bold
    @State private var timeFormatter = ElapsedTimeFormatter(showSubseconds: false)
    
    var body: some View {
        Text(NSNumber(value: elapsedTime), formatter: timeFormatter)
            .fontWeight(fontWeight)
            .onChange(of: showSubseconds) {
                timeFormatter.showSubseconds = $0
            }
            .onAppear(perform: {
                timeFormatter = ElapsedTimeFormatter(showSubseconds: showSubseconds)
            })
    }
}

class ElapsedTimeFormatter: Formatter {
    let componentsFormatter: DateComponentsFormatter = {
        let formatter = DateComponentsFormatter()
        formatter.allowedUnits = [.minute, .second, .hour]
        formatter.zeroFormattingBehavior = .pad
        return formatter
    }()
    var showSubseconds: Bool
    init(showSubseconds: Bool) {
        self.showSubseconds = showSubseconds
        super.init()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    override func string(for value: Any?) -> String? {
        guard let time = value as? TimeInterval else {
            return nil
        }
        
        guard let formattedString = componentsFormatter.string(from: time) else {
            return nil
        }
        
        if showSubseconds {
            let hundredths = Int((time.truncatingRemainder(dividingBy: 1)) * 100)
            let decimalSeparator = Locale.current.decimalSeparator ?? "."
            return String(format: "%@%@%0.2d", formattedString, decimalSeparator, hundredths)
        }
        
        return formattedString
    }
}
struct SeizureView_Previews: PreviewProvider {
    static var previews: some View {
        SeizureView(apptCardVM: ApptCardViewModel())
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM