swiftuiforeachswiftui-listswiftui-navigationlinkswiftui-navigationview

swiftui how to remove chevron on navigation link list in 2023?


Is there a cleaner solution different by this answer and this answerto remove those chevrons on lists? Maybe today apple improved it (even not using navigationStack) the kind of issue.

I know there is same question, but is relatively a bit old, and I'm wondering if there is a better solution today, still not using navigation stack. the first given solution seems to be a bit slow respect standard code, and a bit too verbose.

my starting code

import SwiftUI

struct FakeCar: Identifiable, Hashable {
    let id = UUID().uuidString
    let name: String
}


struct DevelopingView: View {

    let test: [FakeCar] = [
        FakeCar(name: "car 1"),
        FakeCar(name: "car 2")
    ]

    @State private var tappedItem: FakeCar?

    var body: some View {
        
        NavigationView {
            List {
                //standard code with chevron, I do not want it.
                Section (header: Text("Base")) {
                    ForEach(test) { item in
                        NavigationLink(destination: Text("destination: \(item.name)") ) {
                            Text(item.name)
                        }
                    }
                }
                
                //solution from apple site, but adding vstack elements are not "centered" vertically due to navigation link
                //✅ SOLUTION this version is upgraded by me, I needed to remove the default chevron, adding VStack with its options, and opacity and frame to empty navigtionLink
                Section (header: Text("test 1 - ✅ solution")) {
                    ForEach(test) { item in
                        ZStack {
                            VStack(alignment: .leading, spacing: 0) {
                                NavigationLink(destination: Text("destination: \(item.name)")) { EmptyView() }
                                    .opacity(0.0)
                                    .frame(height:0)
                                Text(item.name)
                            }
                        }
                    }
                }

                Section (header: Text("test 1.1 - ✅✅ better solution")) {
                    ForEach(test, id: \.self) { item in
                        ZStack(alignment: .leading) {
                            NavigationLink(destination: Text("destination: \(item.name)")) {
                                EmptyView()
                            }
                            .opacity(0)
                            Text(item.name)
                        }
                    }
                }
                
                //another solution i tested, but elements are "centered" and do not know how to adjust it
                Section (header: Text("test 2")) {
                    ForEach(test, id: \.self) { item in
                        ZStack {
                            NavigationLink(destination: Text("destination: \(item.name)")) {
                                EmptyView()
                            }
                            .opacity(0)
                            .buttonStyle(PlainButtonStyle())

                            Text(item.name)
                        }
                    }
                }

            }
        }
        //solution found on stack, works, but is a bit verbose and unclear
        NavigationView{
            List{
                ForEach(test) { item in
                    Button(action: { tappedItem = item }) {   // << activate !!
                        Text("Select: \(item.name)")
                            .padding(.vertical,3)
                    }
                }
            }
            .background(
                NavigationLink(destination: Text("MessageDetailView \(tappedItem?.name ?? "N/D")"),
                               isActive: Binding(
                                get: { tappedItem != nil },         // << handle !!
                                set: { _,_ in tappedItem = nil }
                               )){
                                   EmptyView()
                               }
            )
        }
    }
}




//gives Identifiable tho stringgs
extension String: Identifiable {
    public typealias ID = Int
    public var id: Int {
        return hash
    }
}

SOLUTION based on the Sweeper answer, adding empty view, opacity and alignment

NavigationView {
            
            List {
                
                Section(header: Text("Available")) {
                    if classFromEntryPoint.availableApps.isEmpty {
                        Text("No app Available")
                    } else {
                        ForEach(classFromEntryPoint.availableApps) { app in
                            ZStack(alignment: .leading) {
                                VStack(alignment: .leading, spacing: 0) {
                                    NavigationLink(destination: StoreAppDetailView(selectedApp: app)) { EmptyView() }
                                        .opacity(0.0)
                                    RowCell(app: app)
                                }
                            }
                        }
                    }
                }
}
}

Solution

  • In iOS 17, you can use navigationDestination(item:destination:) to bind the navigation destination to a variable. That means you can just have Buttons in your list, setting the variable to different values.

    @State var tappedItem: FakeCar? = nil
    
    NavigationStack { // this does not work with the old NavigationView!
        List {
            Section {
                ForEach(test) { item in
                    Button {
                        self.tappedItem = item
                    } label: {
                        Text(item.name)
                    }
                    .tint(.black)
                }
            }
        }
        .navigationDestination(item: $tappedItem) { item in
            Text("destination: \(item.name)")
        }
    }
    

    That said, this becomes rather cumbersome when you have multiple types of data (not just FakeCars). You might need an enum with associated values, and switch on it in navigationDestination to compute what destination it should be.

    As for the solution with the ZStack, you just need to say alignment: .leading for the alignment to work:

    ZStack(alignment: .leading) {
        NavigationLink(destination: Text("destination: \(item.name)")) {
            EmptyView()
        }
        .opacity(0)
        Text(item.name)
    }