Launching a Sheet from a NavigationLink in SwiftUI
Recently I’ve been exploring a re-write of JiffyCV, ditching the original React Native and Expo approach and going fully native on iOS using SwiftUI and SwiftData.
As part of this exploration I’ve been looking at how I would use SwiftUI’s List and Detail UX pattern to show the original experience navigational hierarchy of:
- Experience Category List
- Experience List
- Experience Version List
- Experience Version Detail
In the React Native app I used a slide-in menu for the Experience Category List so there were less levels of nested navigation and they could switch between Experience Category from within one of the List or Detail screens.
In SwiftUI the slide-in menu pattern is part of the SplitView pattern with iOS apps showing the Sidebar menu as another level of navigation, on iPadOS and MacOS however the Sidebar acts in a slide-in menu like behaviour. SplitView applications work best with a two-level navigation structure.
According to the Apple Human Interface Guidelines for Sidebars they suggest using a tab bar instead of a Sidebar so using a slide-in menu approach on iOS would go against the design language of the platform.
With these constraints in mind I had to find a way to reduce the levels of navigation in the SwiftUI version of my app.
One way I achieved this was to use a Sheet to show the Detail screen instead of taking the user yet another level deeper in the navigation when they press an item in a List view.
Launching a Sheet from a NavigationLink
I wanted the List view to give the user the same look and feel of any other List view they’d used, with the ability to search within the list, delete items in the list, swipe to carry out actions on list items and to be able to add a new item.
This meant using the same List and NavigationLink set up I had used on the other List views but I wanted pressing a NavigationLink to have a different outcome, to launch the Sheet instead of navigating the user away.
To achieve this I wrapped the NavigationLink in a Button and set the NavigationLink’s destination to EmptyView() which would mean that it would look like another NavigationLink but clicking it wouldn’t go anywhere, however because it was wrapped in the Button that would capture the press and allow me to launch the Sheet.
One small style tweak I had to do was to set foregroundStyle to .black , this was because where the NavigationLink was pointing to an empty view it would show as grey instead of black.
Summary
Exploring how the navigation of a SwiftUI rewrite will work has been interesting and I’ve iterated on a number of interactivity designs that make me realise how clunky the old JiffyCV app feels.
While it can feel a little inconvenient for the SplitView to not work past two levels of navigation it has forced me to be a little more creative in the UX of the app which is never a bad thing.