Search code examples
iosswiftswiftuinavigationuinavigationcontroller

How to navigate from a navigation view to another navigation view with button click in Swift


I am trying to navigate from one navigation view to another with button click. The flow should be Content View -> Login View -> Password View. I successfully navigated from Content to Login by appending to navigation path, but when I tried to do the same within Login View to go to Password, it doesn't work. Is this the right way to navigate between views? I am not sure why the following doesn't work for Login -> Password.

   ... Content View
   @State private var path = NavigationPath()
    var body: some View {
        NavigationStack(path: $path) {
            ZStack {                    
                ...some view
                Button(action: {path.append("LoginView")}) {
                                Image(systemName: "arrow.right")
                                    .frame(width:30, height: 30)
                                    .background(.white.opacity(0.5))
                                    .foregroundColor(.white)
                                    .clipShape(Circle()).padding(.top, 580)
                            }.navigationDestination(for: String.self) { view in
                                if view == "LoginView" {
                                    LoginView()
                                }
                                else {
                                    PasswordView()
                                }
                            }
            }
        }
    }

// login page
struct LoginView: View {
    @State private var username: String = ""
    @State private var path = NavigationPath()
    
    var body: some View {
            NavigationView {
              NavigationStack {
                ZStack {
                    VStack {
                        Button(action: {path.append("PasswordView")}) {
                            Text("Continue")
                                .padding(.horizontal, 125)
                                .padding(.vertical, 10)
                                .font(.system(size: 16, weight: .medium))
                                .foregroundColor(Color("DarkGreen"))
                                .background(Color("PrimaryGreen"))
                                .cornerRadius(25)
                        }.navigationDestination(for: String.self) { view in
                                if view == "PasswordView" {
                                    PasswordView()
                                }
                            }
                    }
               }
            }
          }
    }


// password page
struct PasswordView: View {
    @State private var password: String = ""
    var body: some View {
        NavigationView {
            ZStack {
               ...password view
            }
        }
    }
    
}



Solution

  • Try this approach where you have only one NavigationStack and a binding to control which View you want to display:

    The reasons it does not work with your current code is because you have multiple NavigationView (which are deprecated BTW), and multiple NavigationStack to display the desired Views. Use just one NavigationStack.

    struct ContentView: View {
        @State private var path = NavigationPath()
        
        var body: some View {
            NavigationStack(path: $path) {
                ZStack {
                    Text("ContentView")
                    Button(action: {path.append("LoginView")} ) {
                        Image(systemName: "arrow.right")
                            .frame(width:30, height: 30)
                            .background(.white.opacity(0.5))
                            .foregroundColor(.blue)
                            .clipShape(Circle()).padding(.top, 580)
                    }
                    .navigationDestination(for: String.self) { view in
                        if view == "LoginView" {
                            LoginView(path: $path)  // <--- here
                        }
                        else {
                            PasswordView()
                        }
                    }
                }
            }
        }
    }
    
    struct LoginView: View {
        @State private var username: String = ""
        @Binding var path: NavigationPath  // <--- here
        
        var body: some View {
            ZStack {
                VStack {
                    Button(action: {path.append("PasswordView")}) {
                        Text("Continue")
                            .padding(.horizontal, 125)
                            .padding(.vertical, 10)
                            .font(.system(size: 16, weight: .medium))
                            .foregroundColor(Color.green)
                            .background(Color.white)
                            .cornerRadius(25)
                    }
                }
            }
        }
    }
    
    struct PasswordView: View {
        @State private var password: String = ""
        
        var body: some View {
            ZStack {
                Text("PasswordView")
            }
        }
    }