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"
                }
            }
        }
    }
}

Weitere Artikel