I have written a lot of SwiftUI Code and deployed it to production in three of my apps in the App Store in the last months.
There is one issue I have struggled with multiple times: NavigationLink
. A NavigationLink
allows you to declare navigation betweens views in a NavigationView
stack with a simple push animation.
Automatic visual changes
SwiftUI tries to be helpful and automatically applies visual changes to the view that triggers the navigation: A Text
becomes blue or a row in a List
gets a disclosure indicator to make it clear for the user that tapping the element is going to trigger an action.
This is definetely the behaviour I want as a developer in most cases and it saves me time when SwiftUI takes care of that. However sometimes I do want to have complete control over the look and feel of the view triggering a navigation.
Paul Hudson made me aware of a pretty nice trick in his video about NavigationView
in SwiftUI.
The idea: Use EmptyView!
The main idea behind this trick is using an EmptyView
as the content view of the NavigationLink
. This will make it invisible in the rendered layout. The navigation is then triggerd by a state variable.
struct ContentView: View {
@State private var isShowingDetailView = false
var body: some View {
NavigationView {
VStack {
NavigationLink(destination: Text("DetailView"), isActive: $isShowingDetailView) {
EmptyView()
}
Button("Tap to show detail") {
self.isShowingDetailView = true
}
}
}
}
}
Programmatic navigation
Another cool thing about this trick is that it can also be used to trigger a navigation programatically (for example based on an API request that runs asynchronously). Just change the state variable and the navigation is triggered.
Multiple navigation targets
This approach is also easily expandable to multiple navigation targets. Instead of using a Bool
toggle we can use a String
as a selection
to identify the navigation to trigger.
struct ContentView: View {
@State private var selection: String? = nil
var body: some View {
NavigationView {
VStack {
NavigationLink(destination: Text("Map"), tag: "Map", selection: $selection) {
EmptyView()
}
NavigationLink(destination: Text("Forecast"), tag: "Forecast", selection: $selection) {
EmptyView()
}
Button("Tap to show map") {
self.selection = "Map"
}
Button("Tap to show forecast") {
self.selection = "Forecast"
}
}
}
}
}