2

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
        })
    }
}
ej5607
  • 309
  • 2
  • 12
  • Does this answer your question? [swiftui subview reappear after click the back button and update state data](https://stackoverflow.com/questions/67650235/swiftui-subview-reappear-after-click-the-back-button-and-update-state-data) – aheze Jul 28 '21 at 15:15
  • Did you try `.toolbar`? – lorem ipsum Jul 28 '21 at 19:37
  • Hi @loremipsum - Thank you for for your suggestion. I tried the following things with .toolbar: I put the NavigationLink from the SecondView to the ThirdView in the .toolbar command, I put the button to inactivate the ThirdView and move back to the SecondView in a .toolbar command, and then I tried each one alone and both together. Unfortunately, none of these caused the ThirdView to change when I clicked the button to inactivate that view and move back to the SecondView. Please let me know if there's something else I could try with .toolbar or if you meant something different. – ej5607 Jul 29 '21 at 12:49
  • Hi @aheze - Thank you for sending that question-and-answer. That question is a bit different from mine, however I did try creating a second NavigationView in the SecondView. My understanding is that this shouldn't be necessary because I thought child views inherited the NavigationView from a parent view (but I'm new to this and could be wrong). Nonetheless, although adding an additional NavigationView added a second navigation bar row to the SecondView and ThirdView, my "Back to 2nd View" button still did inactivate and dismiss the ThirdView. – ej5607 Jul 29 '21 at 13:02

1 Answers1

3

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
            }
        )
    }
}

First view to second view to third view, then reversed

aheze
  • 24,434
  • 8
  • 68
  • 125