Reputation: 1063
I don't understand the issues around how SwiftUI and DispatchQueues work. Here is the code.
This works and will continue to refresh the counter state variable every second.
// Example 1 - This works
struct TimerButtonTest : View {
@State var counter: Int = 0
var body: some View {
start()
return VStack { Text("\(counter)") }
}
func start() {
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(1000)) {
self.counter += 1
}
}
}
This does not. After the button is pressed, the counter increments by 1 (after 1 second) but stops.
// Example 2 - This does not work
struct TimerButtonTest : View {
@State var counter: Int = 0
var body: some View {
return Button(action: {self.start()}, label: {Text("\(counter)")})
}
func start() {
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(1000)) {
self.counter += 1
}
}
}
Why doesn't the counter continue incrementing like the first example?
Upvotes: 4
Views: 5639
Reputation: 114975
In the first piece of code you call start
each time the body
of the button is evaluated.
start
(eventually) updates counter
which is the state that is bound to the button's Text
. Updating the bound state causes SwiftUI to evaluate the body
var again. This calls start
, which calls the asyncAfter
and the process repeats indefinitely. As Rob pointed out in the comments, this isn't a very good approach - body
might be called at any time and any number of times, so you can't guarantee that you will only get an update once per second.
In the second piece of code you only call start
when the button is tapped. counter
will be updated after a second and the Text
updated. Nothing further will happen until you tap the button again.
Upvotes: 6