Navigation in SwiftUI

Back in July I wrote a post about Navigation in SwiftUI trying to explain the difference between NavigationLink and DynamicNavigationDestinationLink. The latter has been deprecated, so the article is now useless, you can still find it here https://www.gfrigerio.com/navigation-in-swiftui-deprecated/
I updated the repository on GitHub to remove the deprecated code and provide a few examples of various type of NavigationLink

NavigationLink

If you followed some tutorials made in June 2019 you’d probably seen it called NavigationButton, but has been deprecated pretty soon and it is now called NavigationLink.
It is similar to a Button, but allows you to specify a destination and once the button is pressed the new View is pushed into the stack. Note that you have to be part of a NavigationView to use the NavigationLink, you don’t need to specify NavigationView in all of your views, but you can’t use NavigationLink if you’re not part of a navigation stack.
Let’s see an example


NavigationLink(destination: MyModal()) {
    Text("push view")
}

so once you tap on push view a new view is pushed on the screen.
NavigationLink is great when you need to respond to a tap on the screen and you know where to go, but sometimes you want to show a new view programmatically, for example you may need to wait for an asynchronous operation to end before pushing a new view and you can’t do it with a “simple” NavigationLink. All the examples can be seen in this file

NavigationLink isActive

One of the possibilities you have to programmatically push a new view is using NavigationLink(destination:isActive:).


@State var showView = false
...
NavigationLink(destination: MyModal(), isActive: $showView) {
}

So you can open the view MyModal by setting showView to true, for example in a Button action or when something happens, like you fetched some data and now is ready. As you can see there is a view associated with the link, so the only way I fount was to pass an empty view builder, so there’s nothing on screen. That’s the only downside, and DynamicNavigationDestinationLink didn’t require to create an empty view, so I hope they’ll provide a way to push views programmatically in a more elegant way in the future.

NavigationLink with tag

If you have a few different views you need to push programmatically I don’t think having some Bool variables is a great solution, as you’d need one for each view and you may end up with a duplicate code or a serie of if statements. Luckily there is a third implementation of NavigationLink that provides a better solution, NavigationLink(destination:tag:selection:).


@State var tag:Int? = nil
...
NavigationLink(destination: MyModal(), tag: 1, selection: $tag) {
}
...
Button(action: {
    self.tag = 1
}, label: {
    Text("show view tag 1")
})

Again, we need to have an empty view, but this time we can specify a tag and a binding variable for the selection. Once the variable tag is set to 1 MyModal is pushed on the navigation stack. You can use a Int, or another type conforming to Hashable, so you can have an enum of different views and use it for the tag if you want something more elegant and readable.

Sheet

If you want to present a sheet instead of pushing a view you can use .sheet at the end of your view hierarchy.


@State var showModal = false
...
var body: some View {
// your view here
}.sheet(isPresented: $showModal) {
    MyModal()
}

and this works like NavigationLink with isActive, once you set showModal to true the sheet is presented. There is no tag equivalent for a sheet, but you can have multiple sheets with different variables.

Hope you liked my quick introduction on how to programmatically push views in SwiftUI, happy coding 🙂