Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SwiftUI NavigationLink: Navigate to a destination view AFTER running account creation code successfully

Tags:

swiftui

I want to run account creation logic and then, if successful, transition to the destination view. Otherwise, I'll present an error sheet. NavigationLink transitions immediately to the destination view on tap.

I can get it to work if I create a phantom NavigationLink using the isActive overload and an empty string as the text (which creates a view with a zero frame). Then I toggle the isActive property with a Button presented to the user that runs the account creation logic first and at the end of the chain toggles the NavigationLink to active. I am inside a NavigationView.

        @State private var isActive: Bool = false

        NavigationView {

            // Name, Email, Password Textfields here

            // Button to run account creation logic:

            Button(action: {
                // Account creation promise chain here, then...
                self.isActive.toggle()
            }) {
                Text("Create Account")
            }

            // Phantom navigation link:

            NavigationLink("", destination: VerifyEmailView(email: email), isActive: self.$isActive)

        }

Is there a better way to do this? It seems bad practice to trigger running the account creation logic from a button, and then activate a phantom navigation link to transition to the next screen.

like image 397
Joe Sweeney Avatar asked Aug 28 '19 18:08

Joe Sweeney


People also ask

What are navigation links in Swift UI?

One of the cool things that are wrapped into navigation links is that Swift UI automatically adds a back button on the secondary view and will know to navigate back to the previous view when selected. Navigation links make connecting views a breeze with Swift UI!

How to prevent immediate invocation of navigationlinks in SwiftUI?

Building off of Mohit's answer, making a little more Swifty with an enum that encapsulates screen state: Show activity on this post. You can wrap your Destination View in a lazy view to prevent the immediate invocation. Here's an example: I've written a full blog post covering this a few other pitfalls of NavigationLinks in SwiftUI. Refer here.

How to change the title of a navigation view in SwiftUI?

By attaching the title to whatever is inside the navigation view, SwiftUI can change the title as its content changes. Tip: You can use navigationTitle () on any view inside the navigation view; it doesn’t need to be the outermost one.

Is there a way to show workouts in SwiftUI navigation?

Bookmark this question. Show activity on this post. I've read a lot here about navigation in SwiftUI and tried a couple of things, but nothing is working as desired. Basically, I have a view with a list of workouts and you can show a single workout by clicking on a row. This works as expected using NavigationView together with NavigationLink.


3 Answers

There is a very simple approach to handle your views' states and NavigationLinks. You can notify your NavigationLink to execute itself by binding a tag to it. You can then set-unset the tag and take control of your NavigationLink.

struct SwiftView: View {
@State private var actionState: Int? = 0

var body: some View {

    NavigationView {
                VStack {
                    NavigationLink(destination: Text("Destination View"), tag: 1, selection: $actionState) {
                        EmptyView()
                    }


                    Text("Create Account")
                        .onTapGesture {
                            self.asyncTask()
                    }
                }
            }
    }

func asyncTask() {
    //some task which on completion will set the value of actionState
    self.actionState = 1
}

}

Here we have binded the actionState with our NavigationLink, hence whenever the value of actionState changes, it will be compared with the tag associated with our NavigationLink.

Your Destination View will not be visible until you set the value of actionState equal to the tag associated with our NavigationLink.

Like this you can create any number of NavigationLinks and can control them by changing just one Bindable property.

Thanks, hope this helps.

like image 69
mohit kejriwal Avatar answered Oct 16 '22 04:10

mohit kejriwal


Building off of Mohit's answer, making a little more Swifty with an enum that encapsulates screen state:


enum ActionState: Int {
    case setup = 0
    case readyForPush = 1
}

struct SwiftView: View {
    @State private var actionState: ActionState? = .setup

    var body: some View {

        NavigationView {
                VStack {
                    NavigationLink(destination: SomeView, tag: .readyForPush, selection: $actionState) {
                        EmptyView()
                    }


                    Text("Create Account")
                        .onTapGesture {
                            self.asyncTask()
                    }
                }
            }
       }

func asyncTask() {
    //some task which on completion will set the value of actionState
    self.actionState = .readyForPush
}
like image 33
Zack Shapiro Avatar answered Oct 16 '22 05:10

Zack Shapiro


You can wrap your Destination View in a lazy view to prevent the immediate invocation. Here's an example:

struct LazyView<Content: View>: View {
    let build: () -> Content
    init(_ build: @autoclosure @escaping () -> Content) {
        self.build = build
    }
    var body: Content {
        build()
    }
}

Eventually, invoke it like this:

NavigationLink(destination: LazyView(Text("Detail Screen"))){//your code}

I've written a full blog post covering this a few other pitfalls of NavigationLinks in SwiftUI. Refer here.

like image 28
AnupamChugh Avatar answered Oct 16 '22 03:10

AnupamChugh