Home > database >  Swift Nested Navigation Links popping using selection Environment object
Swift Nested Navigation Links popping using selection Environment object

Time:05-06

I have a NavigationView and it has 3 views I want to navigate to successively. Each view has a NavigationLink within it. Ideally, I want:

View 1 -> View 2 -> View 3

But what I get is: View 1 -> View 2 -> View 3 (1sec) -> View 1

I am using the tagged selection method for the NavigationLink, and the selection is from a @Published EnvironmentObject.

Here's the code:

import SwiftUI

struct ContentView: View {
    
    @EnvironmentObject var viewModel: ViewModel

    var body: some View {
        
        VStack {
            NavigationLink(destination: secondPage(), tag: "View 2", selection: $viewModel.selection) {
                Button(
                    action: {
                        viewModel.selection = "View 2"
                    }, label: {
                        Text("view 2")
                    }
                )
                .navigationTitle("View 1")
            }
            .isDetailLink(false)
        }
    }
}



struct secondPage: View {
    
    @EnvironmentObject var viewModel: ViewModel
    
    var body: some View {

        NavigationLink(destination: thirdPage(), tag: "View 3", selection: $viewModel.selection) {
            Button(
                action: {
                    viewModel.selection = "View 3"
                },
                label: {
                    Text("View 3")
                }
            )
            .navigationTitle("View 2")
        }
        .isDetailLink(false)
    }
}


struct thirdPage: View {
    
    var body: some View {
        VStack {
            Text("third page")
        }
        .navigationTitle("View 3")
    }
}

class ViewModel: ObservableObject {
    @Published var selection: String? = nil
}

import SwiftUI

@main
struct testApp: App {
    
    @StateObject var viewModel = ViewModel()
    
    var body: some Scene {
        WindowGroup {
            NavigationView {
                ContentView()
            }
            .navigationViewStyle(StackNavigationViewStyle())
            .environmentObject(viewModel)
        }
    }
}

I use a button and the viewModel because in my real code I need the code to perform an action.

Help would be so appreciated!

CodePudding user response:

Welcome to StackOverflow! The problem you are having is very similar to this question, but you are using a different mechanism. The reason for the pop back is that you are using the same variable to handle both NavigationLinks, so, instead of:

View 1 -> View 2 -> View 3

what is actually happening is:

View 1(viewModel.selection = "View 2") -> View 2

Then, when you try to navigate again:

View 2(viewModel.selection = "View 3")

You are actually changing your original link to

View 1(viewModel.selection = "View 3") -> View 2

as well. The OS gets confused and pops back to the first link(SecondPage). To fix it, simply use multiple variables to keep the navigation state:

struct ContentView: View {
    @EnvironmentObject var viewModel: PageViewModel

    var body: some View {
        
        VStack {
            NavigationLink(destination: secondPage(), tag: "View 2", selection: $viewModel.selection2) {
                Button(
                    action: {
                        viewModel.selection2 = "View 2"
                    }, label: {
                        Text("view 2")
                    }
                )
                .navigationTitle("View 1")
            }
            .isDetailLink(false)
        }
    }
}

struct secondPage: View {
    
    @EnvironmentObject var viewModel: PageViewModel
    
    var body: some View {

        NavigationLink(destination: thirdPage(), tag: "View 3", selection: $viewModel.selection3) {
            Button(
                action: {
                    viewModel.selection3 = "View 3"
                },
                label: {
                    Text("View 3")
                }
            )
            .navigationTitle("View 2")
        }
        .isDetailLink(false)
    }
}

struct thirdPage: View {
    
    var body: some View {
        VStack {
            Text("third page")
        }
        .navigationTitle("View 3")
    }
}

class PageViewModel: ObservableObject {
    @Published var selection2: String? = nil
    @Published var selection3: String? = nil
}
  • Related