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.
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!
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.
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.
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.
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.
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
}
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With