NavigationStack in SwiftUI (iOS 16)

Ruchish shah
3 min readJun 7, 2022

--

Programmatic navigation is always challenge in SwiftUI. With iOS 16 and SwiftUI 4.0, finally we got similar to UINavigationController called NavigationStack which act as root view and manage stack of views over a root view.

Note: NavigationView is deprecated as of iOS 16, in favour of NavigationStack

Pushing View

  • User can add views to the top of the stack by clicking or tapping a NavigationLink

Popping View

  • Pushed Views will be removed or popped using built-in, platform-appropriate controls, like a Back button or a swipe gesture.

Note: The stack always displays the most recently added view that hasn’t been removed, and doesn’t allow the root view to be removed.

How To

Step 1: To create navigation links, associate a view with a data type by adding a navigationDestination(for:destination:) modifier inside the stack’s view hierarchy.

NavigationStack {    

List(parks) { park in
...
}
.navigationDestination(for: Park.self) { park in
ParkDetails(park: park)
}
}

Step 2: Initialize a NavigationLink that presents an instance of the same kind of data. The following stack displays a ParkDetails view for navigation links that present data of type Park:

NavigationStack {    

List(parks) { park in
NavigationLink(park.name, value: park)
}
.navigationDestination(for: Park.self) { park in
ParkDetails(park: park)
}
}

Here, List act as root view for NavigationStack and as indicated earlier, its always visible.

  • Selecting a navigation link from the list pushes a ParkDetails view to the stack.
  • Navigating back using Back Button, removes the detail view and reveals the root view i.e. list again.
  • The system disables backward navigation controls when the stack is empty i.e. when root view is visible.

Control Navigation State of NavigationStack

By Default, a navigation stack manages state to keep track of the views on the stack.

Programatically can control of the state by initializing the NavigationStack with a binding to a collection of data values that we create.

Internally, the stack

  • adds items to the collection as it adds views to the stack
  • removes items from the collection when it removes views.
@State 
private var presentedParks: [Park] = [] // Initializing the state as an empty array indicates a stack with no views.
NavigationStack(path: $presentedParks) {

List(parks) { park in
NavigationLink(park.name, value: park)
}
.navigationDestination(for: Park.self) { park in
ParkDetails(park: park)
}
}

Behind the scene:

When user taps or clicks the navigation link for a park, the stack displays the ParkDetails view using the associated park data. Stack automatically puts the park data in the presentedParks array.

Modify Stack State to Change View on NavigationStack

func showParks() {    
presentedParks = [Park("Park 1"), Park("Park 2")]
}

Above code changes NavigationStack withPark 2 as last item pushed on stack.

You can use a path to support deep links, state restoration, or other kinds of programmatic navigation.

Navigate to Different View Types

So far we saw example where we were only pushing ParkDetail with associated Park data. In order to manage navigation to different view types, add multiple navigationDestination(for:destination:) modifiers inside the stack’s view hierarchy, with each modifier presenting a different data type.

  • The stack matches navigation links with navigation destinations based on their respective data types.

Creating a Navigation Stack with a Path

To create a navigation stack with homogeneous navigation state that you can control, use following initializer of NavigationStack

init(path: Binding<Data>, root: () -> Root)

To create a navigation stack with heterogeneous navigation state that you can control, use following initializer of NavigationStack

init(path: Binding<NavigationPath>, root: () -> Root)

References:

--

--

Ruchish shah

Apple Platform Philosophical Software Engineer — iOS, iPadOS, tvOS, macOS, watchOS