In the code below, changes to the destination
value aren't captured by ViewA
if:
ViewB
is navigated to by changing thedestination
property ofViewModel
.ViewA
is embedded in aNavigationLink
or anothernavigationDestination
.
For example, the return button in ViewB
doesn't work when accessed from ContentWithinNavigationLink
, but it works from ContentDirect
.
I've also noticed that the code works if I move the presentingViewB
property to the ViewModel
class. However, in real code, that significantly increases the complexity of ViewModel
, as I have more than ten destinations to handle. Each bound with different shapes of data. I'd prefer to store an enum value in ViewModel
, listen to it in ViewA
, and then convert it to boolean flags and properties in ViewA
—just for iOS 16 compatibility. For iOS 17 and above, the enum works perfectly with navigationDestination(item:destination:)
.
class ViewModel: ObservableObject {
@Published var destination = ""
func gotoB() {
destination = "ViewB"
}
func reset() {
destination = ""
}
}
struct ContentWithinNavigationLink: View {
var body: some View {
NavigationStack {
NavigationLink {
ViewA()
} label: {
Text("Goto A")
}
}
}
}
struct ContentDirect: View {
var body: some View {
NavigationStack {
ViewA()
}
}
}
struct ViewA: View {
@State private var presentingViewB = false
@StateObject private var viewModel = ViewModel()
var body: some View {
Button {
viewModel.gotoB()
} label: {
Text("goto ViewB")
}
.navigationDestination(isPresented: $presentingViewB) {
ViewB().environmentObject(viewModel)
}
.onChange(of: viewModel.destination) { newValue in
if newValue == "ViewB" {
presentingViewB = true
} else {
presentingViewB = false
}
}
}
}
struct ViewB: View {
@EnvironmentObject var viewModel: ViewModel
var body: some View {
Button {
viewModel.reset()
} label: {
Text("Return")
}
.navigationBarBackButtonHidden()
}
}
The reason of using code like
Button {
viewModel.gotoB()
} label: {
Text("goto ViewB")
}
in ViewA is because in real code the Button is actually several subviews where the navigation-triggering code is deeply nested.