I recently tried to migrate from SwiftUI NavigationView to NavigationStack, but experienced some problems with bindings. If the view stack has changes, my bindings seem to convert into some sort of local state instead of bindings.
I was able to recreate the issue in a much simplified version of my code. In this simplified version I have @State counter
in CounterView
which creates a new counter each time the view is loaded. The counter
is passed as a binding to EditCoutnerView
. This binding is broken when if the NavigationStack changes.
To recreate this I have to navigate as follows:
I can't figure out why the binding is broken. Any ideas what to do?
import SwiftUI
@main
struct PlaygroundAppApp: App {
@StateObject var router: Router = .init()
var body: some Scene {
WindowGroup {
NavigationStack(path: self.$router.path) {
Counters()
}
.environmentObject(self.router)
}
}
}
struct Other: Hashable {}
struct New: Hashable {}
final class Router: ObservableObject {
@Published var path = NavigationPath()
}
struct Counters: View {
@EnvironmentObject var router: Router
var body: some View {
VStack {
NavigationLink("New Counter", value: New())
NavigationLink("Other Counters", value: Other())
.padding()
}
.navigationTitle("Main")
.navigationDestination(for: New.self) { examination in
CounterView()
}
.navigationDestination(for: Other.self) { examination in
OtherCounter()
}
}
}
struct OtherCounter: View {
var body: some View {
VStack {
NavigationLink("Counter", value: New())
}
.navigationTitle("Other Counter")
}
}
struct CounterView: View {
@State private var counter: Int = 0
var body: some View {
VStack {
NavigationLink("Edit counter", value: counter)
.padding()
Text("COUNT \(self.counter)")
}
.navigationDestination(for: Int.self, destination: { value in
EditCounterView(count: self.$counter)
})
.navigationTitle("Counter")
}
}
struct EditCounterView: View {
@Binding var count: Int
var body: some View {
VStack {
Text("Current count \(count)")
Button("increase") {
self.count += 1
}
.padding()
Button("decrease") {
self.count -= 1
}
}
.navigationTitle("Edit Counter")
}
}
As lorem ipsum pointed out, NavigationLink with destination and label fixes this issue.