I am curious if there is a way to get the timers to update with the UI (as they are now) even while someone is scrolling. Additionally, I want to make sure that as the UI updates each second the screen does not freeze. This question has been updated in response to the helpful answers I received previously.
struct ContentView: View {
var body: some View {
NavigationView{
NavigationLink(destination: ScrollTest()){
HStack {
Text("Next Screen")
}
}
}
}
}
struct ScrollTest: View {
@ObservedObject var timer = SectionTimer(duration: 60)
var body: some View {
HStack{
List{
Section(header: TimerNavigationView(timer: timer)){
ForEach((1...50).reversed(), id: \.self) {
Text("\($0)…").onAppear(){
self.timer.startTimer()
}
}
}
}.navigationBarItems(trailing: TimerNavigationView(timer: timer))
}
}
}
struct TimerNavigationView: View {
@ObservedObject var timer: SectionTimer
var body: some View{
HStack {
Text("\(timer.timeLeftFormatted) left")
Spacer()
}
}
}
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
}
}