In the code below, changes to the destination value aren't captured by ViewA if:
ViewB is navigated to by changing the destination property of ViewModel.
ViewA is embedded in a NavigationLink or another navigationDestination.
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.