Reputation: 4200
I have the following timer:
struct ContentView: View {
@State var timeRemaining = 10
let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
var body: some View {
NavigationView{
VStack {
if(self.timeRemaining > 0) {
Text("\(timeRemaining)")
.onReceive(timer) { _ in
if self.timeRemaining > 0 {
self.timeRemaining -= 1
}
}
} else {
Text("Time is up!")
}
}
}
}
}
If I remove the NavigationView
view, the timer updates and works, but like that it doesn't, what's going on here and how can I update it while in the NavigationView
? Or is there a better practice?
Thanks
Upvotes: 3
Views: 1161
Reputation: 11
İ had same problem but not one-to-one. I Solved like this : my ContentView has .onApper {binancemanager.fetch()} —-> i call here but i moved it to @main struct MyApp:{WindowsGroup.Vstack.Content.
Upvotes: 0
Reputation: 258237
It is better to attach observers to non-conditional views, like
var body: some View {
NavigationView{
VStack {
if(self.timeRemaining > 0) {
Text("\(timeRemaining)")
} else {
Text("Time is up!")
}
}
.onReceive(timer) { _ in // << to VStack
if self.timeRemaining > 0 {
self.timeRemaining -= 1
}
}
}
}
Update: some thoughts added (of course SwiftUI internals are known only for Apple).
.onReceive must be attached to persistently present view in NavigationView, the reason is most probably in conditional ViewBuilder and internal of NavigationView wrapper around UIKit UINavigationControler.
if you remove the condition and have a single Text view with onReceive inside the VStack inside the NavigationView, nothing is ever received
If after condition removed it is attached to Text("\(timeRemaining)")
then all works (tested with Xcode 11.4), because there is state dependency in body.
If it is attached to constant Text then there is nothing changed in body, ie. dependent on changed state - timeRemaining
, so SwiftUI rendering engine interprets body as static.
Upvotes: 3