I have a navigation view consisting of the main ContentView and a TimerView. The TimerView has a timer which correctly increments and also correctly stops when I call self.timer.upstream.connect().cancel()
.
However when I go back to ContentView and then navigate to TimerView again, I want the timer to start counting again however this does not happen. secondsElapsed
does reset to 0 but the timer doesn't run.
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView {
NavigationLink(destination: TimerView()) {
Text("Go to Timer View")
}
}
}
}
struct TimerView: View {
@State var secondsElapsed = 0
var timer = Timer.publish (every: 1, on: .main, in: .common).autoconnect()
var body: some View {
VStack {
Text("\(self.secondsElapsed) seconds elapsed")
Button("Stop timer",
action: {
self.timer.upstream.connect().cancel()
})
}.onReceive(timer) { _ in
self.secondsElapsed += 1
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
timer.upstream.connect().cancel()randomElement()! If we want to resume the timer, we will have to make some adjustments. Resuming timer requires reinitialization of the Timer so we will change our timer to a State property. fillColor = colors.
When you press the view, a timer should start counting upwards, and tapping again stops the timer. Finally, when you tap again to start the timer, the timer should begin at 0.
If you want to run some code regularly, perhaps to make a countdown timer or similar, you should use Timer and the onReceive() modifier. It's important to use . main for the runloop option, because our timer will update the user interface.
To my knowledge (and historically), Timer cannot be restarted after cancellation or invalidation.
What I do is just re-declare the timer.
The previous code was from SwiftUI 1, which had some "oddities," especially around lifecycle. So, I updated the code to how I would do it in SwiftUI 3.
For this code, you must import combine
to declare a Cancellable
type.
Here is an example with some added personal preferences:
struct TimerView: View {
@State var secondsElapsed = 0
@State var timer: Timer.TimerPublisher = Timer.publish(every: 1, on: .main, in: .common)
@State var connectedTimer: Cancellable? = nil
var body: some View {
VStack {
Text("\(self.secondsElapsed) seconds elapsed")
Button("Stop Timer", action: {
self.cancelTimer()
})
Button("Continue Timer", action: {
self.instantiateTimer()
})
Button("Restart Timer", action: {
self.restartTimer()
})
}.onAppear {
self.instantiateTimer()
}.onDisappear {
self.cancelTimer()
}.onReceive(timer) { _ in
self.secondsElapsed += 1
}
}
func instantiateTimer() {
self.timer = Timer.publish(every: 1, on: .main, in: .common)
self.connectedTimer = self.timer.connect()
return
}
func cancelTimer() {
self.connectedTimer?.cancel()
return
}
func resetCounter() {
self.secondsElapsed = 0
return
}
func restartTimer() {
self.secondsElapsed = 0
self.cancelTimer()
self.instantiateTimer()
return
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With