Jake
Jake

Reputation: 13753

Go to a new view using SwiftUI

I've got a basic view with a button using SwiftUI and I'm trying to present a new screen/view when the button is tapped. How do I do this? Am I suppose to create a delegate for this view that will tell the app's SceneDelegate to present a new view controller?

import SwiftUI

struct ContentView : View {
    var body: some View {
        VStack {
            Text("Hello World")
            Button(action: {
                //go to another view
            }) {
                Text("Do Something")
                    .font(.largeTitle)
                    .fontWeight(.ultraLight)
            }
        }
    }
}

Upvotes: 107

Views: 211390

Answers (16)

Norberto Carbonell
Norberto Carbonell

Reputation: 1

This has been answered multiple times but never afais gives the response starting by pressing a Button to get to the next View. Adding this approach just in case someone ends up (just like me) on a Google search that has this answer as one the top links.

This approach is a combination of @State variable on the Initial View and the same variable as @Binding in the Ending View. You can always add a "Back" button on the ending View to return to the original.

struct InitialView : View {
    @State private var isEndingViewActive = false
    HStack {
        var body : some View {
            if isEndingViewActive {
                EndingView(isEndingViewActive : $isEndingViewActive)
            } else {
                Button(“Going to End View”) { isEndingViewActive = true }
            }
        }
    }
}

struct EndingView : View {
    @Binding private var isEndingViewActive : Bool
    HStack {
        var body : some View {
            Button(“Going Back to Initial View”) { isEndingViewActive = false }
        }
    }
}

Upvotes: -1

bulutgonulalan
bulutgonulalan

Reputation: 356

I think this is the easiest and clear way. Use fullScreenCover after UI tool.

Button(action: {
    // code
}) {
    Text("Send")
}.fullScreenCover(isPresented: self.$model.goToOtherView, content: {
    OtherView()
})

Upvotes: 8

Theo
Theo

Reputation: 1

This is my approach to solving this in SwiftUI using NavigationLink and tags. I'm pretty new to programming so please be polite :)

  1. Create Custom Button to be implemented later on:

    struct CustomButton: View {
    
    @State private var selection: String? = nil
    
    var title: String
    var subtitle: String
    var tag: String
    
    var body: some View {
    
        NavigationLink(destination: TheoryView(), tag: "A", selection: $selection) {EmptyView()}
        NavigationLink(destination: PatternsView(), tag: "B", selection: $selection) { EmptyView() }
        NavigationLink(destination: Text("Online Shop"), tag: "C", selection: $selection) { EmptyView() }
        NavigationLink(destination: Text("Calendar"), tag: "D", selection: $selection) { EmptyView() }
    
        Button(action: {
            selection = tag
        }) {
            VStack(alignment: .center) {
                Text(title)
    
                    .font(.title2)
                    .fontWeight(.bold)
                    .foregroundColor(.red)
    
                Text(subtitle)
    
                    .font(.subheadline)
                    .fontWeight(.medium)
                    .foregroundColor(.white)
            }
            .fixedSize(horizontal: true, vertical: false)
            .frame(width: 200)
        } 
    }
    

    }

  2. Implement Custom Button into ContentView:

    struct ContentView: View {
    var body: some View {
        NavigationView {
            VStack {
                Group {
                    Spacer()
                    Image("logo")
                        .resizable()
                        .scaledToFit()
                        .frame(width: 180, height: 180)
    
                    Text("")
    
                    CustomButton(title: "TEORIA", subtitle: "Teoria Taekwon - DO", tag: "A")
                    CustomButton(title: "UKŁADY FORMALNE", subtitle: "Układy Formalne", tag: "B")
                    CustomButton(title: "SKLEP ONLINE", subtitle: "Nasz sklep Online", tag: "C")
                    CustomButton(title: "KALENDARZ", subtitle: "Kalendarz Imprez", tag: "D")
    
                    Spacer()
                }
               .padding(.vertical, -12)
               .buttonStyle(BlueCapsule())
    
            }
        }
    }
    

    }

Upvotes: -1

Sajjad Sarkoobi
Sajjad Sarkoobi

Reputation: 1064

iOS 16, SwiftUI 4

NavigationStack

If you are going to support iOS 16 and above, you can use the benefits that NavigationStack is offering.

You can see it as a stack that can push your views into. So it will be so easier to navigate a user through the app.

If this is our detailed view that we want to move here:

struct DetailView: View {
   var body: some View {
     Text("You are at detail view")
  }
}

First approach is By using the NavigationLink like before. We can move to a new view.

struct ContentView: View {
var body: some View {
    NavigationStack {
        VStack {
            NavigationLink {
                DetailView()
            } label: {
                Text("Show Detail")
            }
        }
        .navigationTitle("Navigation")
    }
   }
}

The second approach will use the value inside the NavigationLink. You can pass a value and then define .navigationDestination based on value types. as soon as user trigger that link SwiftUI will know which navigationDestination should be responsible for handling that.

struct ContentView: View {
@State var fruits: [String] = ["🍎", "🍊", "🍌", "🥝"]
var body: some View {
    NavigationStack {
        VStack(spacing: 20) {
            NavigationLink("Navigate: String Value", value: "New Page")
            
            NavigationLink("Navigate: Int Value", value: 1)
            
            ForEach(fruits, id:\.self) { fruit in
                NavigationLink(fruit, value: fruit)
            }
        }
        .navigationDestination(for: String.self) { value in
            Text("New screen")
            Text("Value is String -> \(value)")
        }
        .navigationDestination(for: Int.self) { value in
            Text("New screen")
            Text("Value is Integer -> \(value)")
        }
    }
}
}

Third approach NavigationStack shows its worth by defining a path. We can define a path and control our navigation based on the type. It will be so much easier to navigate programmatically. We can add views or remove them from the path list. It will also be so helpful when we want to navigate back to the root view by making the path list empty.

struct ContentView: View {

@State var path: [String] = []
@State var fruits: [String] = ["🍎", "🍊", "🍌", "🥝"]

var body: some View {
    NavigationStack(path: $path) {
        ZStack {
            Button("Random fruits") {
                let random = fruits.randomElement()!
                path.append(random)
            }
        }
        .navigationDestination(for: String.self) { value in
            VStack(spacing: 20) {
                Text("New screen")
                Text("Value is String -> \(value)")
                
                Button("Random fruits") {
                    let random = fruits.randomElement()!
                    path.append(random)
                }
                
                Button("Back to Root") {
                    path.removeAll()
                }
            }
        }
    }
  }
}

enter image description here

Upvotes: 12

Helal Khan
Helal Khan

Reputation: 887

var body: some View {
        
        NavigationView{
            Button(action: {
                print("Login Button click")
            }){
                NavigationLink(destination: DestinationView()) {
        
                    Text("Login")
                        .foregroundColor(.black)
                        .frame(width: 220, height: 50)
                        .background(Color.green)
                }
            }
        }
    }

struct DestinationView: View {
    var body: some View {
        Text("Hello, World!")
    }
}

Upvotes: 0

George
George

Reputation: 30341

This was not intended to be the top answer here - but rather an alternative method if you didn't want the navigation bar. See Jake's answer below for the normal way to do this with NavigationView & NavigationLink. Hopefully this is still useful or points you in the right direction too.

If you are just trying to get a NavigationLink working with a Binding, you can use the same NavigationLink init I did with the isActive binding parameter.

Anyway, back to the answer…


I made a view modifier for this. It also means that there is no navigation bar. You can call it like so:

.navigate(to: MainPageView(), when: $willMoveToNextScreen)

This can be attached to anything, so I typically attach it to the end of the body, for example:

@State private var willMoveToNextScreen = false

var body: some View {
    VStack {
        /* ... */
    }
    .navigate(to: MainPageView(), when: $willMoveToNextScreen)
}

Code (remember to import SwiftUI):

extension View {
    /// Navigate to a new view.
    /// - Parameters:
    ///   - view: View to navigate to.
    ///   - binding: Only navigates when this condition is `true`.
    func navigate<NewView: View>(to view: NewView, when binding: Binding<Bool>) -> some View {
        NavigationView {
            ZStack {
                self
                    .navigationBarTitle("")
                    .navigationBarHidden(true)

                NavigationLink(
                    destination: view
                        .navigationBarTitle("")
                        .navigationBarHidden(true),
                    isActive: binding
                ) {
                    EmptyView()
                }
            }
        }
        .navigationViewStyle(.stack)
    }
}

Upvotes: 86

Edward Brey
Edward Brey

Reputation: 41638

To programmatically navigate to a link without displaying a visual element, overlay one of your views with a hidden NavigationLink.

In this sample, the UI will navigate when some code sets shouldNavigate to true:

struct ProgramaticSample {
    @State var shouldNavigate = false
    
    var body: some View {
        Text("Hello navigation")
            .overlay(NavigationLink(
                destination: DestinationScreen(),
                isActive: $shouldNavigate) {}
                .hidden())
    }
}

Upvotes: 1

Varun Naharia
Varun Naharia

Reputation: 5420

We can use Text inside Navigation and make it like Button enter image description here http://g.recordit.co/CkIkjvikfu.gif

struct HomeContent: View {
    var body: some View {
        NavigationView{
            VStack {
                NavigationLink(
                destination: LoginContentView()) {
                    
                        
                           
                    Text("Login")
                        .font(.title)
                        .foregroundColor(Color.white)
                        .multilineTextAlignment(.center)
                        .frame(width: 300.0, height: 50.0)
                        .background(Color(UIColor.appLightBlue))
                   
                }
                
            }
        }
    }
}

Upvotes: 1

Mohsen mokhtari
Mohsen mokhtari

Reputation: 3062

if don't want to show the navigationView you can hide it in destination.

struct ContentViewA : View {
    var body: some View {
        NavigationView {
            VStack {
                Text("Hello World")
                NavigationLink(destination: ContentViewB()) {
                    Text("Go To Next Step")
                }
            }
        }
    }
}



struct ContentViewB : View {
        var body: some View {
            NavigationView {
                VStack {
                    Text("Hello World B")

                }.navigationBarTitle("")
                .navigationBarHidden(true)
            }
        }
    }

or if you want to hide it based on conditions you can use @State for changing the visibility.

Upvotes: 14

Braver Chiang
Braver Chiang

Reputation: 381

I think Jake's answer is the basic way to NextView.

And I think the way bellow is a simple, formal, and dynamic way to try, if you really need to hit a BUTTON. According to Paul Hudson's Video, from 10'00" to 12'00".

(should go to 12'00" to 15'00", if you want to go to different views by tapping different buttons.) (should go to 15'00" to 16'00", if you want to go to second view and go back automatically.) And more

And here is the code example.

import SwiftUI

struct ContentView: View {

    @State var areYouGoingToSecondView: Bool // Step 2

    var body: some View {
        NavigationView{ // Step 1

            VStack {

                // Step 3
                NavigationLink(destination: YourSecondView(), isActive: $areYouGoingToSecondView) { EmptyView() }


                Text("Hello World")

                Button(action: {
                    self.areYouGoingToSecondView = true // Step 4

                }) {
                    Text("Do Something (Go To Second View)")
                    .font(.largeTitle)
                    .fontWeight(.ultraLight)
                }
            }
        }
    }
}

Upvotes: 8

Dharmesh Mansata
Dharmesh Mansata

Reputation: 4708

Now we can use NavigationLink


File A:

struct ContentView: View {
var body: some View {
    NavigationView {
        VStack {
            Text("Hello World")
            NavigationLink(destination: secondView()) {
                Text("Hit Me!")
                    .fontWeight(.semibold)
                    .font(.title)
                    .padding()
                    .foregroundColor(.white)
                    .background(LinearGradient(gradient: Gradient(colors: [Color(.white),Color(.blue)]), startPoint: .leading, endPoint: .trailing))
                    .cornerRadius(40)
        }
      }
    }
  }
}

File B:

struct secondView: View {
var body: some View {
    VStack {
    VStack(alignment: .leading) {
        Text("Turtle Rock")
            .font(.title)
        HStack(alignment: .top) {
            Text("Joshua Tree National Park")
                .font(.subheadline)
            Spacer()
            Text("California")
                .font(.subheadline)
        }
    }
    .padding()
        Spacer()

    }
  }
}                                                                                                  

Upvotes: 3

Jake
Jake

Reputation: 13753

The key is to use a NavigationView and a NavigationLink:

import SwiftUI

struct ContentView : View {
    var body: some View {
        NavigationView {
            VStack {
                Text("Hello World")
                NavigationLink(destination: DetailView()) {
                    Text("Do Something")
                }
            }
        }
    }
}

Upvotes: 92

Edward
Edward

Reputation: 2974

You can no longer use NavigationButton. Instead you should use NavigationLink.

struct ContentView: View {
    var body: some View {
        NavigationView {
            NavigationLink(destination: DetailView()) {
                Text("Push new screen")
            }
        }
    }
}

Upvotes: 1

nikoclicks
nikoclicks

Reputation: 179

The OP has been answered multiple times here but I just wanted to also demonstrate the cool aspects of SwiftUI by showing if you have view A with data that view B will also be using, you can pass data by creating a @State in view A and declaring the same variable with @Binding declaration in view B

struct ViewA : View {
    @State var myItems: [Items]
    var body: some View {
        NavigationView {
            VStack {
                NavigationButton(destination: ViewB(items: $myItems)) {
                    Text("Go To ViewB")
                }
            }
        }
    }
}
struct ViewB : View {
    @Binding var myItems: [Items]
    var body: some View {
        NavigationView {
            List{
                ForEach(myItems.identified(by: \.self)) {
                    Text($0.itemName)
                }
            }.navigationBarTitle(Text("My Items"))
        }
    }
}

Upvotes: 3

SMP
SMP

Reputation: 1669

Here's another way to present a view WITHOUT using NavigationView. This is like UIKit's UIModalPresentationStyle.currentContext.

struct PresenterButtonView: View {
var body: some View {
    PresentationButton(Text("Tap to present"),
                       destination: Text("Hello world"))
}}

Upvotes: 3

Kathiresan Murugan
Kathiresan Murugan

Reputation: 2962

Have to create a DetailView like LandmarkDetail() and call a NavigationButton with destination as LandmarkDetail(). Now detail view was open.

For passing values to detail screen means. by sending like below code.

struct LandmarkList: View {
    var body: some View {
        NavigationView {
            List(landmarkData) { landmark in
                NavigationButton(destination: LandmarkDetail()) {
                    LandmarkRow(landmark: landmark)
                }
            }
            .navigationBarTitle(Text("Landmarks"))
        }
    }
}

Upvotes: -1

Related Questions