Search code examples
listswiftui

Initiate non-pushing action from List/Form row, with NavigationLink styling and behaviour


I have a List. I want to initiate an action when the user taps on a list row.

The row needs to look and behave like a regular NavigationLink, complete with chevron. But, a tap should not inititate a push to another screen.

Note: this pattern is used by Apple itself. For example, in the Reminders app:

Reminders app non-pushing row

(Tapping this row will cause a sheet to be displayed, rather than a new screen being pushed on to the navigation stack.)

Crucially, this row looks and behaves as it were a NavigationLook, including highlighting the entire row on tap, with a standard-looking chevron.

Question: how can I reproduce this row?


Solution

  • You can use a NavigationLink as the label of a Button.

    struct ContentView: View {
        @State private var showSheet = false
        
        var body: some View {
            NavigationStack {
                List {
                    Button {
                        showSheet = true
                    } label: {
                        NavigationLink("Details") {}
                    }
                    // remove the button's default tint
                    .tint(.primary)
                    
                    // a normal navigation link for comparison
                    NavigationLink("Details") {}
                }
                .sheet(isPresented: $showSheet) {
                    Text("Sheet content...")
                }
            }
        }
    }
    

    Output:

    enter image description here