NavigationView and NavigationLink on button click

2020-08-17 06:57发布

问题:

I am trying to push from login view to detail view but not able to make it.even navigation bar is not showing in login view. How to push on button click in SwiftUI? How to use NavigationLink on button click?

var body: some View {
    
    NavigationView {
              
            VStack(alignment: .leading) {
                       
                     Text("Let's get you signed in.")
                       .bold()
                       .font(.system(size: 40))
                       .multilineTextAlignment(.leading)
                       .frame(width: 300, height: 100, alignment: .topLeading)
                       .padding(Edge.Set.bottom, 50)

                     
                       Text("Email address:")
                           .font(.headline)
                                      TextField("Email", text: $email)
                                       .frame(height:44)
                                       .accentColor(Color.white)
                                       .background(Color(UIColor.darkGray))
                                       .cornerRadius(4.0)
                       
                       Text("Password:")
                                      .font(.headline)
                       
                      SecureField("Password", text: $password)
                                   .frame(height:44)
                                   .accentColor(Color.white)
                                   .background(Color(UIColor.darkGray))
                                   .cornerRadius(4.0)
                       
                       
                       Button(action: {
                           print("login tapped")
                           
                       }) {
                           HStack {
                               Spacer()
                               Text("Login").foregroundColor(Color.white).bold()
                               Spacer()
                           }
                       }
                       .accentColor(Color.black)
                       .padding()
                       .background(Color(UIColor.darkGray))
                       .cornerRadius(4.0)
                       .padding(Edge.Set.vertical, 20)
                   }
                   .padding(.horizontal,30)
           }
            .navigationBarTitle(Text("Login"))
        }  

回答1:

To fix your issue you need to bind and manage tag with NavigationLink, So create one state inside you view as follow, just add above body.

@State var selection: Int? = nil

Then update your button code as follow to add NavigationLink

NavigationLink(destination: Text("Test"), tag: 1, selection: $selection) {
    Button(action: {
        print("login tapped")
        self.selection = 1
    }) {
        HStack {
            Spacer()
            Text("Login").foregroundColor(Color.white).bold()
            Spacer()
        }
    }
    .accentColor(Color.black)
    .padding()
    .background(Color(UIColor.darkGray))
    .cornerRadius(4.0)
    .padding(Edge.Set.vertical, 20)
}

Meaning is, when selection and NavigationLink tag value will match then navigation will be occurs.

I hope this will help you.



回答2:

Another approach:

SceneDelegate

if let windowScene = scene as? UIWindowScene {
            let window = UIWindow(windowScene: windowScene)
            window.rootViewController = UIHostingController(rootView: BaseView().environmentObject(ViewRouter()))
            self.window = window
            window.makeKeyAndVisible()
        }

BaseView

import SwiftUI

struct BaseView : View {

    @EnvironmentObject var viewRouter: ViewRouter

    var body: some View {
        VStack {
            if viewRouter.currentPage == "view1" {
                FirstView()
            } else if viewRouter.currentPage == "view2" {
                SecondView()
                    .transition(.scale)
            }
        }
    }
}

#if DEBUG
struct MotherView_Previews : PreviewProvider {
    static var previews: some View {
        BaseView().environmentObject(ViewRouter())
    }
}
#endif

ViewRouter

import Foundation
import Combine
import SwiftUI

class ViewRouter: ObservableObject {

    let objectWillChange = PassthroughSubject<ViewRouter,Never>()

    var currentPage: String = "view1" {
        didSet {
            withAnimation() {
                objectWillChange.send(self)
            }
        }
    }
}

FirstView

import SwiftUI

struct FirstView : View {

    @EnvironmentObject var viewRouter: ViewRouter

    var body: some View {
        VStack {
            Button(action: {self.viewRouter.currentPage = "view2"}) {
                NextButtonContent()
            }
        }
    }
}

#if DEBUG
struct FirstView_Previews : PreviewProvider {
    static var previews: some View {
        FirstView().environmentObject(ViewRouter())
    }
}
#endif

struct NextButtonContent : View {
    var body: some View {
        return Text("Next")
            .foregroundColor(.white)
            .frame(width: 200, height: 50)
            .background(Color.blue)
            .cornerRadius(15)
            .padding(.top, 50)
    }
}

SecondView

import SwiftUI

struct SecondView : View {

    @EnvironmentObject var viewRouter: ViewRouter

    var body: some View {
        VStack {
            Spacer(minLength: 50.0)
            Button(action: {self.viewRouter.currentPage = "view1"}) {
                BackButtonContent()
            }
        }
    }
}

#if DEBUG
struct SecondView_Previews : PreviewProvider {
    static var previews: some View {
        SecondView().environmentObject(ViewRouter())
    }
}
#endif

struct BackButtonContent : View {
    var body: some View {
        return Text("Back")
            .foregroundColor(.white)
            .frame(width: 200, height: 50)
            .background(Color.blue)
            .cornerRadius(15)
            .padding(.top, 50)
    }
}

Hope this helps!



回答3:

The accepted answer uses NavigationLink(destination:tag:selection:) which is correct.

However, for a simple view with just one NavigationLink you can use a simpler variant: NavigationLink(destination:isActive:)


Usage #1

NavigationLink is activated by a standard Button:

struct ContentView: View {
    @State var isLinkActive = false

    var body: some View {
        NavigationView {
            VStack(alignment: .leading) {
                ...
                NavigationLink(destination: Text("OtherView"), isActive: $isLinkActive) {
                    Button(action: {
                        self.isLinkActive = true
                    }) {
                        Text("Login")
                    }
                }
            }
            .navigationBarTitle(Text("Login"))
        }
    }
}

Usage #2

NavigationLink is hidden and activated by a standard Button:

struct ContentView: View {
    @State var isLinkActive = false

    var body: some View {
        NavigationView {
            VStack(alignment: .leading) {
                ...
                Button(action: {
                    self.isLinkActive = true
                }) {
                    Text("Login")
                }
            }
            .navigationBarTitle(Text("Login"))
            .background(
                NavigationLink(destination: Text("OtherView"), isActive: $isLinkActive) {
                    EmptyView()
                }
                .hidden()
            )
        }
    }
}

Usage #3

NavigationLink is hidden and activated programatically:

struct ContentView: View {
    @State var isLinkActive = false

    var body: some View {
        NavigationView {
            VStack(alignment: .leading) {
                ...
            }
            .navigationBarTitle(Text("Login"))
            .background(
                NavigationLink(destination: Text("OtherView"), isActive: $isLinkActive) {
                    EmptyView()
                }
                .hidden()
            )
        }
        .onAppear {
            self.isLinkActive = true
        }
    }
}