I'm a novice Xcode/SwiftUI developer and have been stuck on the following problem related to navigation. In the following minimum, reproducible example, I want the user to be able to navigate backwards through the app view-by-view after reaching the ThirdView as follows: ThirdView -> SecondView -> FirstView (or ContentView, in the example below).
When the NavigationLink in the SecondView to reach the ThirdView is in the body of the SecondView, everything works as expected. (This is the SecondView code I've commented out in the example below.) However, if I move that same NavigationLink up to the navigation bar (as shown), clicking the "Back to 2nd View" bar button in the ThirdView does nothing. The view does not change. Is there a way to navigate from SecondView -> ThirdView via a navigation link in the bar button but still be able to progress backwards from the ThirdView -> SecondView, as well?
I've read some question-and-answers to issues like this online that suggest that this problem is a glitch in the Xcode Simulator, but when I load the app on my device, I have the same problem.
struct ContentView: View {
@State private var activeLink: Bool = false
var body: some View {
NavigationView {
VStack {
Spacer()
NavigationLink("Show Second Screen",
destination: SecondView(active: $activeLink), isActive: $activeLink)
Spacer()
}.padding()
.navigationBarTitle("First view")
}
}
}
struct SecondView: View {
@Binding var active: Bool
@State private var thirdViewLink: Bool = false
var body: some View {
VStack {
Spacer()
// This works as expected but is not ideal for my purposes
/* NavigationLink("Show Third View",
destination: ThirdView(thirdViewActive: $thirdViewLink), isActive: $thirdViewLink) */
Spacer()
}.padding()
.navigationBarTitle("Second View")
.navigationBarBackButtonHidden(true)
.navigationBarItems(leading: Button("Back to 1st View") {
self.active = false
}, trailing: NavigationLink("Show Third View",
destination: ThirdView(thirdViewActive: $thirdViewLink),
isActive: $thirdViewLink))
}
}
struct ThirdView: View {
@Binding var thirdViewActive: Bool
var body: some View {
VStack(spacing: 15) {
Text("Third View")
Spacer()
}.padding()
.navigationBarItems(leading: Button("Back to 2nd View") {
self.thirdViewActive = false
})
}
}
The problem is that you have a NavigationLink
outside your NavigationView
.
.navigationBarItems(leading: Button("Back to 1st View") {
self.active = false
}, trailing: NavigationLink( /// this is not inside your NavigationView!
"Show Third View",
destination: ThirdView(thirdViewActive: $thirdViewLink),
isActive: $thirdViewLink
)
This will not work and you'll run into weird issues. NavigationLink
always needs to be inside NavigationView
. You should follow your approach with the "This works as expected but is not ideal for my purposes", and just pass in an EmptyView
to hide it.
struct SecondView: View {
@Binding var active: Bool
@State private var thirdViewLink: Bool = false
var body: some View {
VStack {
Spacer()
// This works as expected but is not ideal for my purposes
NavigationLink(destination: ThirdView(thirdViewActive: $thirdViewLink), isActive: $thirdViewLink) {
EmptyView()
}
Spacer()
}.padding()
.navigationBarTitle("Second View")
.navigationBarBackButtonHidden(true)
.navigationBarItems(leading: Button("Back to 1st View") {
self.active = false
}, trailing:
Button("Show Third View") {
self.thirdViewLink = true
}
)
}
}