SwiftUI NavigationLink Hide Arrow
Asked Answered
M

23

56

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

Maintenon answered 11/10, 2019 at 2:33 Comment(3)
also curious about this, did you find an answer?Ecchymosis
@Ecchymosis nah, I went back to storyboard, UICollectionView, and segues on cell clicks after this.Maintenon
See: #56516833Donor
B
45

The way it worked for me:

List { 
    ForEach(elements) { element in
        ZStack {
            CustomView(element: element)
            NavigationLink(destination: DestinationView()) {
                EmptyView()
            }.buttonStyle(PlainButtonStyle())
        }
    }
}
Bloemfontein answered 20/1, 2020 at 23:49 Comment(4)
I used Rectangle().opacity(0.0) instead of EmptyView() for List, it worked.Shebashebang
I'm using the same solution but the problem is that even though the arrow is not visible, you can still tap it. In my application I have to disable this otherwise it will crash. Any ideas?Hollinger
.buttonStyle(PlainButtonStyle()) is working for me.Spermaceti
Doesn't work on iOS14.2+Extravehicular
T
65

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!

Tamratamsky answered 18/2, 2021 at 14:37 Comment(6)
The only working answer I've found. Thanks !Overbite
Still works with iOS 15Almeida
Still works with iOS 16Cankerous
This should be the accepted answer. (no offense)Giraffe
Still works with iOS 17Trajectory
This worked on Xcode 15.2 betaOrthodox
B
45

The way it worked for me:

List { 
    ForEach(elements) { element in
        ZStack {
            CustomView(element: element)
            NavigationLink(destination: DestinationView()) {
                EmptyView()
            }.buttonStyle(PlainButtonStyle())
        }
    }
}
Bloemfontein answered 20/1, 2020 at 23:49 Comment(4)
I used Rectangle().opacity(0.0) instead of EmptyView() for List, it worked.Shebashebang
I'm using the same solution but the problem is that even though the arrow is not visible, you can still tap it. In my application I have to disable this otherwise it will crash. Any ideas?Hollinger
.buttonStyle(PlainButtonStyle()) is working for me.Spermaceti
Doesn't work on iOS14.2+Extravehicular
S
24

I got it done with this

NavigationLink(destination: DestinationView()) {
      EmptyView()
}
.frame(width: 0, height: 0)
.hidden()
Supernova answered 11/5, 2020 at 7:38 Comment(3)
.hidden() should be enoughIconolatry
I had to include the frame along with hidden because it caused a Text to truncate its contents when the NavigationLink was to the right of the Text in a List row if I just wrote hidden without the zero-size frame, so at least in my case I needed to write it as the answer above says.Indulgence
Doesn't work anymoreExtravehicular
T
7

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)
        }
    }
}
Technic answered 27/9, 2020 at 18:28 Comment(0)
D
6
List { 
    ForEach(elements) { element in
        ZStack {
            CustomView(element: element)
            NavigationLink(destination: DestinationView()) {
                EmptyView()
            }.opacity(0.0)
        }
    }
}
Derryberry answered 13/8, 2021 at 23:59 Comment(0)
P
5

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.

Plumbing answered 25/10, 2022 at 17:41 Comment(0)
S
5

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")
}
Sibell answered 17/11, 2022 at 10:26 Comment(1)
Very clean solution. Also works without the isActive part I thinkGerena
F
3
@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 }
        }
}
Foy answered 17/10, 2019 at 13:50 Comment(4)
This doesn't really work as when you go back it's not possible to select the same element.Bloemfontein
I used navigationlink in this way and everything worked fine.Foy
This doesn't work when using a Form: ` var body: some View { Form { NavigationLink(destination: Text("View"), tag: 1, selection: $selection) { EmptyView() } Text("Tap").onTapGesture { self.selection = 1 } } } ` NOTE: the VStack in the above example isn't needed.Segmentation
Try in this way: VStack { navlink Form}Foy
D
3

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)
    }
}
Dewberry answered 31/10, 2020 at 15:31 Comment(2)
I tried this and it did not work. I still see the arrow on the right.Macnair
This is the one I selected and it works great for me. But I'm sad we still have to hack this.Vouch
S
3

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)
                }
            }
        }
    }
}
Samoyedic answered 13/4, 2022 at 20:46 Comment(0)
A
2

2023 Update

This simple solution works for me:

ZStack {
    CustomCell()  
    NavigationLink(destination: DetailView()) {
     EmptyView()
    }
    .opacity(0)
}
Aristocratic answered 15/2, 2023 at 17:5 Comment(1)
Works in my case, using a NavigationStack, when none of the other answers did.Affricate
H
1

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())
            }
Heterosporous answered 11/1, 2021 at 9:34 Comment(0)
C
1

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) )
Consubstantiate answered 9/7, 2021 at 12:25 Comment(0)
B
1

Use .background modifier.

ForEach(elements) { e in
  AnyViewYouWantToShow(e)
    .background(
      NavigationLink("", destination: DestinationView()))
        .opacity(0)
    )
}
Beshrew answered 31/5, 2022 at 2:16 Comment(1)
This worked for me iOS 15. Just make sure to use the EmptyView() callAttitudinize
G
1

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.

Garcia answered 3/6 at 17:46 Comment(0)
R
0

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))
Religieux answered 29/1, 2021 at 13:57 Comment(1)
Switching the hierarchy (i.e. placing the NavigationLink in the background of your HStack), per the explanation by @turingtested, is better.Francophobe
T
0

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())
        }
    }
}
Thrombosis answered 16/3, 2022 at 9:12 Comment(0)
I
0

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()
    }
}
Intrude answered 8/7, 2022 at 10:2 Comment(1)
'init(isActive:destination:label:)' was deprecated in iOS 16.0: use NavigationLink(value:label:) inside a NavigationStack or NavigationSplitView Deprecated for iOS 16Imputation
L
0

In my case, foregroundColor modifier works for me.

NavigationLink(EmptyView(), label: {
//place your custom view
    Text("your Label")
}).buttonStyle(PlainButtonStyle()).foregroundColor(Color.clear)
Lowerclassman answered 31/7, 2023 at 11:28 Comment(0)
N
0

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)
Nudity answered 25/5 at 10:12 Comment(0)
A
-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)
                    }
                }
Aluminothermy answered 2/4, 2020 at 15:22 Comment(0)
S
-2

It also works with any View (not only Text)

ZStack {
    Text("Some text")
    NavigationLink(destination: Text("Hello")) { 
            EmptyView()
    }.frame(width: 0)
}
Scarface answered 7/5, 2020 at 10:11 Comment(0)
L
-2

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

NavigationLink(
    destination: Text("Destination"),
    label: {}
).opacity(0)
Latten answered 19/7, 2021 at 12:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.