Несмотря на то, что в SwiftUI есть таймер, я не думаю, что его использование является правильным подходом в этом случае.
Ваша модель должна обрабатывать время для вас.
Также помогает, если ваше представление непосредственно наблюдает за своим объектом модели, вместо того, чтобы пытаться наблюдать элемент массива в свойстве вашей наблюдаемой.
Вы не показали свой SectionTimer
, но это то, что я создал:
class SectionTimer:ObservableObject {
private var endDate: Date
private var timer: Timer?
var timeRemaining: Double {
didSet {
self.setRemaining()
}
}
@Published var timeLeftFormatted = ""
init(duration: Int) {
self.timeRemaining = Double(duration)
self.endDate = Date().advanced(by: Double(duration))
self.startTimer()
}
func startTimer() {
guard self.timer == nil else {
return
}
self.timer = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true, block: { (timer) in
self.timeRemaining = self.endDate.timeIntervalSince(Date())
if self.timeRemaining < 0 {
timer.invalidate()
self.timer = nil
}
})
}
private func setRemaining() {
let min = max(floor(self.timeRemaining / 60),0)
let sec = max(floor((self.timeRemaining - min*60).truncatingRemainder(dividingBy:60)),0)
self.timeLeftFormatted = "\(Int(min)):\(Int(sec))"
}
func endTimer() {
self.timer?.invalidate()
self.timer = nil
}
}
Он использует Date
вместо вычитания из оставшегося счетчика; это более точно, поскольку таймер не тикает через определенные промежутки времени. Он обновляет опубликованное свойство timeLeftFormatted
.
Чтобы использовать его, я внес следующие изменения в ваш TimerNavigationView
-
struct TimerNavigationView: View {
@ObservedObject var timer: SectionTimer
var body: some View{
HStack {
Text("\(timer.timeLeftFormatted) left")
Spacer()
}
}
}
Вы можете видеть, как значительно упрощается установка таймера в модели. ваш взгляд.
Вы будете использовать его через .navigationBarItems(trailing: TimerNavigationView(timer: self.test.timers[self.test.currentSection]))
Обновление
Обновленный код в вопросе помог продемонстрировать проблему, и я нашел решение в этом ответе
Когда режим прокрутки прокручивается, режим текущего RunLoop
изменяется и таймер не запускается.
Решение заключается в планировании таймер в режиме common
самостоятельно, а не в режиме default
, который вы получаете с помощью scheduledTimer
-
func startTimer() {
guard self.timer == nil else {
return
}
self.timer = Timer(timeInterval: 0.2, repeats: true) { (timer) in
self.timeRemaining = self.endDate.timeIntervalSince(Date())
if self.timeRemaining < 0 {
timer.invalidate()
self.timer = nil
}
}
RunLoop.current.add(self.timer!, forMode: .common)
}