swiftxcodeswiftuinavigationlink

SwiftUI: NavigationLink is always activated when in a List


I can't prevent SwiftUI's NavigationLink from being activated when in a List, I have this simple piece of code in which I need to do some kind of business check before deciding to show the details page or not (in a real world app, there might be some business logic happens inside the button's action):

struct ContentView: View {
    @State var showDetail = false
    var body: some View {
        NavigationView {
            List {
                Text("Text 1")
                Text("Text 2")
                Text("Text 3")
                NavigationLink(destination: DetailView(), isActive: $showDetail) {
                    LinkView(showDetails: $showDetail)

                }
            }
        }
    }
}

struct LinkView: View {
    @Binding var showDetails: Bool

    var body: some View {
        Button(action: {
            self.showDetails = false
        }) {
            Text("Open Details")
        }
    }
}

struct DetailView: View {
    var body: some View {
        Text("Detail View")
    }
}

how can I prevent navigation link from opening the details page in this case ? and is this a bug in the SDK ?

p.s. XCode version: 13.3.1 and iOS version (real device): 13.3.1

Edit

I can't replace List with ScrollView because I have a ForEach list of items in my real app, so don't post an answer considering using ScrollView.


Solution

  • in a real world app, there might be some business logic happens inside the button's action

    seems to be a little bit alogical.You can simply conditionally disable the link (and inform the user, that the link is unavailable by visual appearance)

    NavigationLink(...).disabled(onCondition)
    

    where

    func disabled(_ disabled: Bool) -> some View
    

    Parameters

    disabled

    A Boolean value that determines whether users can interact with this view.

    Return Value

    A view that controls whether users can interact with this view.

    Discussion

    The higher views in a view hierarchy can override the value you set on this view. In the following example, the button isn’t interactive because the outer disabled(_:) modifier overrides the inner one:

    HStack {
        Button(Text("Press")) {}
        .disabled(false)
    }
    .disabled(true)