NavigationView and NavigationLink on button click in SwiftUI?
Simplest and most effective solution is :
NavigationLink(destination:ScoresTableView()) {
Text("Scores")
}.navigationBarHidden(true)
.frame(width: 90, height: 45, alignment: .center)
.foregroundColor(.white)
.background(LinearGradient(gradient: Gradient(colors: [Color.red, Color.blue]), startPoint: .leading, endPoint: .trailing))
.cornerRadius(10)
.contentShape(Rectangle())
.padding(EdgeInsets(top: 16, leading: UIScreen.main.bounds.size.width - 110 , bottom: 16, trailing: 20))
ScoresTableView is the destination view.
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 programmatically:
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
}
}
}
Here is a GitHub repository with different SwiftUI extensions that makes navigation easier.
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.
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!