blackops
blackops

Reputation: 2394

SwiftUI NavigationLink Hide Arrow

Is there a way to hide the arrow to the right of the navigation link view that is automatically added?

I want to show an image grid using NavigationView -> List -> HStack -> NavigationLink_1 - NavigationLink_2

The NavigationLinks have arrows and it looks weird enter image description here

Upvotes: 63

Views: 50943

Answers (26)

JulioTrujilloH
JulioTrujilloH

Reputation: 11

I'm using SwiftUI with Xcode 15.4. Solution in 2024:

    var body: some View {
    NavigationView {
        List(items){ item in
            Section {
                YourObjectView()
            }
            .listRowSeparator(.hidden) //Removing separator between cells      
            .background{ NavigationLink("", destination: SpecificView(url: "www.yourWebPage.com"))} //Added it as background so that the > (chevron) on the righ side and extra padding disappears
        }
        .navigationTitle("Your List Title")
        .listStyle(.grouped) //Removes boundaries on sides of ListView
        .scrollContentBackground(.hidden) //Removes Background on cells          
    }
}

Added a couple of extra functions that have helped me in my projects.

Upvotes: 0

Narinder Singh
Narinder Singh

Reputation: 196

Working for Swiftui swift 5, ios 17+

   NavigationLink(destination: Destination()) {
                                        Rectangle().opacity(0.0)
                                    }
                                    .opacity(0.0)

.opacity(0) do the job here, without this it will not work.

Upvotes: -2

elemento
elemento

Reputation: 342

.navigationSplitViewStyle(.balanced)

Upvotes: -1

Roman Simenok
Roman Simenok

Reputation: 560

Works for me on iOS 16/17

NavigationLink(destination: YourView) {
                Color.clear // or EmptyView, etc.
            }
            .foregroundStyle(Color.clear)

.opacity(0) also works but in my case I needed .foregroundStyle(Color.clear)

For some reason with .opacity(0) the NavigationLink is not working if I put it outside of the ListView.

Upvotes: 2

Naresh
Naresh

Reputation: 955

To hide the navigation link forward arrow icon we have to do the following steps.

  1. First we need set the navigation link in the overlay of the view.
  2. Second we have to set the navigation link opacity to 0.

Here is the code -

List($store.models, id: \.id) { model in
    CustomListItem(model: model.wrappedValue)
        .overlay {
            NavigationLink {
                DetailView()
            } label: {
                EmptyView()
            }
            .opacity(0) //this will hide the arrow icon.
        }
        .listRowSeparator(.hidden)
}
.listStyle(.plain)

Upvotes: 0

arslan raza
arslan raza

Reputation: 1

In my case, foregroundColor modifier works for me.

NavigationLink(EmptyView(), label: {
//place your custom view
    Text("your Label")
}).buttonStyle(PlainButtonStyle()).foregroundColor(Color.clear)

Upvotes: 0

turingtested
turingtested

Reputation: 7154

The easiest way I've found is to place the navigation in the .background modifier with the opacity of zero:

List {
    Text("The cell")
        .background(
            NavigationLink("", destination: Text("The detail view"))
                .opacity(0)
        )
}

And with this solution you don't loose the dynamic height functionality of the cells.

Bonus: Using .overlay modifier works too!

Upvotes: 76

Sima Nerush
Sima Nerush

Reputation: 87

2023 Update

This simple solution works for me:

ZStack {
    CustomCell()  
    NavigationLink(destination: DetailView()) {
     EmptyView()
    }
    .opacity(0)
}

Upvotes: 2

rapgod
rapgod

Reputation: 86

I've also struggled with this recently and I think I've found a solution by using a custom view for the navigation link (it works for me):

struct CustomNavigationLink<D: View, L: View>: View {
  @ViewBuilder var destination: () -> D
  @ViewBuilder var label: () -> L
  
  @State private var isActive = false
  
  var body: some View {
    Button {
      withAnimation {
        isActive = true
      }
    } label: {
      label()
    }
    .onAppear {
      isActive = false
    }
    .overlay {
      NavigationLink(isActive: $isActive) {
        destination()
      } label: {
        EmptyView()
      }
      .opacity(0)
    }
  }
}

And you use like this:

CustomNavigationLink {
  SomeViewHere()
} label: {
  Text("hello world")
}

Upvotes: 5

Jacob Jidell
Jacob Jidell

Reputation: 2792

Finally found out a way how to avoid the the chevron without doing some tricky ZStacks and other solutions. The only downside is that this is only tested on iOS 16 with the new NavigationPath + NavigationStack.

Instead of using a regular NavigationLink where you apply the hashable object, you'll just use a regular Button and append the object to the NavigationPath.

Example:

@State private var path = NavigationPath()

var body: some View {
    List {
        ForEach(viewModel.customers) { customer in
            Button {
                path.append(customer)
            } label: {
                CustomerCell(customer: customer)
            }
        }
    }
    .navigationDestination(for: Customer.self) { customer in
        CustomerView(customer: customer)
    }
}

For projects using the NavigationBackport (for preparing the new navigation), it might work as well. As you can use NBNavigationPath and append the object to the path with a Button just like the example above.

Upvotes: 8

Cesare
Cesare

Reputation: 930

A lot of examples playing around with ZStack and .opacity but for my opinion SwiftUI can offer more elegant solution using NavigationLink with isActive parameter that works perfect with .listRowSeparator or .listStyle modificators:

struct HidingNavArrowInList: View {
    
    let planets = ["Mars", "Sun", "Mercury", "Venus", "Jupiter", "Uranus", "Saturn", "Earth"]
    
    @State var selectedPlanet: String?
    @State var showDetailView = false
    
    var body: some View {
        NavigationView {
            List {
                ForEach(planets, id: \.self) { planet in
                    Text(planet)
                        .onTapGesture {
                            segue(planet: planet)
                        }
                }
            }
            .background(
                NavigationLink(isActive: $showDetailView, destination: {
                    if let unwrappedPlanet = selectedPlanet {
                        VStack {
                            Text("This is detail view of \(unwrappedPlanet)")
                        }
                    }
                }, label: {
                    EmptyView()
                })
            )
        }
    }
    
    private func segue(planet: String) {
        selectedPlanet = planet
        showDetailView.toggle()
    }
}

Upvotes: 0

Benjamin
Benjamin

Reputation: 10393

Use .background modifier.

ForEach(elements) { e in
  AnyViewYouWantToShow(e)
    .background(
      NavigationLink("", destination: DestinationView()))
        .opacity(0)
    )
}

Upvotes: 1

PatrickDotStar
PatrickDotStar

Reputation: 1794

Setting .opacity(0) on the NavigationLink seems to be the most reliable solution for me because I noticed that it might show the indicators again when messing with the .listStyle property. You will also not lose the highlighted effect.

var body: some View {
    NavigationView {
        List {
            ForEach(items) { item in
                ZStack(alignment: .leading) {
                    NavigationLink(destination: EmptyView()) {
                        EmptyView()
                    }
                    .opacity(0)

                    Text(item.value)
                }
            }
        }
    }
}

Upvotes: 3

Atiar Talukdar
Atiar Talukdar

Reputation: 746

though there is lots of solution. I'm posting my one.

var body: some View {
    VStack{
        List{
            ForEach (items){item in
                switch item.onClick {
                    //For SettingsOverviewView
                case .Settings:
                    ZStack{
                        NavigationLink (destination: SettingsMenuView(item: item)){
                            EmptyView()
                        }
                        .opacity(0.0)
                        .buttonStyle(PlainButtonStyle())
                        
                        //some views that you will show as your listItem
                        HStack {
                           Text(item.name)
                              .font(.body)
                           Spacer()
                        }
                    }
                }
            }
            .listStyle(GroupedListStyle())
        }
    }
}

Upvotes: 0

Mehdi
Mehdi

Reputation: 1356

I set the opacity of the navigationLink to zero and it work like a charm

NavigationLink(
    destination: Text("Destination"),
    label: {}
).opacity(0)

Upvotes: -2

Z3r0CooL
Z3r0CooL

Reputation: 149

List { 
    ForEach(elements) { element in
        ZStack {
            CustomView(element: element)
            NavigationLink(destination: DestinationView()) {
                EmptyView()
            }.opacity(0.0)
        }
    }
}

Upvotes: 7

PerroVerde
PerroVerde

Reputation: 13

Although .background(...).opacity(0) works, in a more complex view it expands itself through all the view and conflicts with other elements like buttons.

If you need it inside a List, what worked for me is also marking the NavigationLink as .disabled(true):

    Text(...)
      .background( NavigationLink(...).opacity(0).disabled(true) )

Upvotes: 1

Amir Khorsandi
Amir Khorsandi

Reputation: 3708

The best workaround for me is using background:

NavigationLink(...) {}
       .opacity(0)
       .background(
         HStack {
           Text("Your custom view without arrow")
         }
       ) 

Or if you need dynamic height as @turingtested posted use NavigationLink as background

Text("Your custom view without arrow")
        .background(NavigationLink( ... ) {}.opacity(0))

Upvotes: 1

Anatoly
Anatoly

Reputation: 121

Only this worked for me, when I tried to implement button tap inside row in List:

ZStack {
                NavigationLink(destination: FlightBoardInformation(flight: flight), tag: FlightBoardNavigation.toFlightDetailed, selection: $navigation) {
                    EmptyView()
                }
                .frame(width: 0, height: 0)
                .hidden()
                .disabled(true)
                Button(action: {
                        self.navigation = .toFlightDetailed
                }) {
                    Text("\(self.flight.airline) \(self.flight.number)")
                }.buttonStyle(PlainButtonStyle())
            }

Upvotes: 1

nullforlife
nullforlife

Reputation: 1414

This is what worked for me, just adding an empty NavigationLink in a ZStack

List(viewModel.items, id: \.id) { item in
    ZStack {
        NavigationLink(destination: Destination()) {}
        CustomView(item: item)
    }
}

Upvotes: 3

Asi Givati
Asi Givati

Reputation: 1475

The only thing that helped me is to add .opacity(0) to NavigationLink like so:

List { 
    ForEach(elements) { element in
        ZStack {
            CustomView(element: element)
            NavigationLink(destination: DestinationView()), 
            label: {}).opacity(0)
        }
    }
}

Upvotes: 9

Jagoan Neon
Jagoan Neon

Reputation: 1128

I got it done with this

NavigationLink(destination: DestinationView()) {
      EmptyView()
}
.frame(width: 0, height: 0)
.hidden()

Upvotes: 23

Roman Mishchenko
Roman Mishchenko

Reputation: 1

It also works with any View (not only Text)

ZStack {
    Text("Some text")
    NavigationLink(destination: Text("Hello")) { 
            EmptyView()
    }.frame(width: 0)
}

Upvotes: -2

Chetan KG
Chetan KG

Reputation: 1

You can also do like: This worked for me,

@State var boolValue: Bool = false


                HStack {
                    Text("Your text")
                    Toggle(isOn: $boolValue){
                        Text("")
                    }
                    if boolValue {
                        NavigationLink(destination: DestinationView()) {
                            EmptyView()
                        }.frame(width: 0)
                    }
                }

Upvotes: -1

Vladimirs Matusevics
Vladimirs Matusevics

Reputation: 1152

The way it worked for me:

List { 
    ForEach(elements) { element in
        ZStack {
            CustomView(element: element)
            NavigationLink(destination: DestinationView()) {
                EmptyView()
            }.buttonStyle(PlainButtonStyle())
        }
    }
}

Upvotes: 46

Narek Ghukasyan
Narek Ghukasyan

Reputation: 259

@State var selection: Int? = nil

var body: some View {
    let navigation = NavigationLink(destination: Text("View"), tag: 1, selection: $selection) { EmptyView() }
    return 
        VStack { 
            navigation
            Text("Tap").onTapGesture { self.selection = 1 }
        }
}

Upvotes: 3

Related Questions