How to delete Navigation Link > symbol in SwiftUI?
Navigation code
NavigationView{
List{
ForEach(messages) { item in
NavigationLink(destination: MessageDetailView()){
ChatRowView(chat: item)
.padding(.vertical,3)
}
}
}
Chevron image of link is injected by List for automatically detected NavigationLink. This is default behavior of List.
The possible solution is to replace NavigationLink inside List with Button and activate NavigationLink programmatically.
Here is a demo of approach. Tested with Xcode 12.4 / iOS 14.4
struct Message: Identifiable, Hashable {
let id: String
}
struct ContentView: View {
let messages = [Message(id: "1"), Message(id: "2"), Message(id: "3")]
#State private var tappedItem: Message?
var body: some View {
NavigationView{
List{
ForEach(messages) { item in
Button(action: { tappedItem = item }) { // << activate !!
Text("Message \(item.id)")
.padding(.vertical,3)
}
}
}
.background(
NavigationLink(destination: Text("MessageDetailView \(tappedItem?.id ?? "")"),
isActive: Binding(
get: { tappedItem != nil }, // << handle !!
set: { _,_ in tappedItem = nil }
)){
EmptyView()
}
)
}
}
}
You should not use List in this case:
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView{
VStack {
ForEach(0..<10) { item in
NavigationLink(destination: Text("some text here!")){
HStack { Text("Link " + item.description); Spacer() }.padding(.horizontal)
}
Divider()
}
Spacer()
}
.navigationTitle("Hello World!")
}
}
}
How to delete Navigation Link > symbol in SwiftUI?
Navigation code
NavigationView{
List{
ForEach(messages) { item in
NavigationLink(destination: MessageDetailView()){
ChatRowView(chat: item)
.padding(.vertical,3)
}
}
}
Chevron image of link is injected by List for automatically detected NavigationLink. This is default behavior of List.
The possible solution is to replace NavigationLink inside List with Button and activate NavigationLink programmatically.
Here is a demo of approach. Tested with Xcode 12.4 / iOS 14.4
struct Message: Identifiable, Hashable {
let id: String
}
struct ContentView: View {
let messages = [Message(id: "1"), Message(id: "2"), Message(id: "3")]
#State private var tappedItem: Message?
var body: some View {
NavigationView{
List{
ForEach(messages) { item in
Button(action: { tappedItem = item }) { // << activate !!
Text("Message \(item.id)")
.padding(.vertical,3)
}
}
}
.background(
NavigationLink(destination: Text("MessageDetailView \(tappedItem?.id ?? "")"),
isActive: Binding(
get: { tappedItem != nil }, // << handle !!
set: { _,_ in tappedItem = nil }
)){
EmptyView()
}
)
}
}
}
You should not use List in this case:
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView{
VStack {
ForEach(0..<10) { item in
NavigationLink(destination: Text("some text here!")){
HStack { Text("Link " + item.description); Spacer() }.padding(.horizontal)
}
Divider()
}
Spacer()
}
.navigationTitle("Hello World!")
}
}
}
I just try this code but I got unexpected edge for List.
If I remove navigationBarItems everything is ok.
import SwiftUI
let numbers: [String] = ["One", "Two", "Three"]
struct NavBarView: View {
var body: some View {
NavigationView {
List(numbers, id: \.self) { number in
SimpleRow(title: number)
}
.navigationBarTitle(Text("Numbers"), displayMode: .inline)
.navigationBarItems(trailing: Button(action: { // add this will affect the List position and size
}, label: {Image(systemName: "plus").imageScale(.medium)}
))
}
}
}
struct SimpleRow: View {
var title: String
var body: some View {
HStack {
Text(title)
Spacer()
}
}
}
Interesting behavior, as it is only when modifier applied directly to List... even can't say if it is a bug... Maybe it is because navigationBarItems has deprecated.
Anyway here is possible solution - attach navigation bar items to something else, like background.
Tested with Xcode 12 / iOS 14
struct NavBarView: View {
var body: some View {
NavigationView {
List(numbers, id: \.self) { number in
SimpleRow(title: number)
}
.background(Color.clear
.navigationBarItems(trailing: Button(action: {
}, label: {Image(systemName: "plus").imageScale(.medium)}
)))
.navigationBarTitle(Text("Numbers"), displayMode: .inline)
}
}
}
I want to add a Button(action: - view to a List view with a ForEach in it which has a NavigationLink in it. So a user can look at a detail screen of list which has a Button(action: - however if they if user does not need to see details they can select using Button in list.
List {
ForEach(DATA, id: \.self { data in
NavigationLink(destination: DetailView(data: data)) {
HStack {
Text(data.name)
Button(action: {
// add to an array
}) {
Text(data.price)
}
}
}
}
}
but when i click on the button always take to DetailView
Not sure if it possible. Any advice
Here is possible approach
List {
ForEach(DATA, id: \.self { data in
NavigationLink(destination: DetailView(data: data)) {
HStack {
Text(data.name)
Button(action: {
// add to an array
}) {
Text(data.price) // some padding might be useful
.foregroundColor(Color.blue) // << can be any
}.buttonStyle(PlainButtonStyle()) // << here !!
}
}
}
}
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
The way it worked for me:
List {
ForEach(elements) { element in
ZStack {
CustomView(element: element)
NavigationLink(destination: DestinationView()) {
EmptyView()
}.buttonStyle(PlainButtonStyle())
}
}
}
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.
I got it done with this
NavigationLink(destination: DestinationView()) {
EmptyView()
}
.frame(width: 0, height: 0)
.hidden()
#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 }
}
}
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)
}
}
}
List {
ForEach(elements) { element in
ZStack {
CustomView(element: element)
NavigationLink(destination: DestinationView()) {
EmptyView()
}.opacity(0.0)
}
}
}
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)
}
}
}
}
}
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)
}
}
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())
}
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) )
Use .background modifier.
ForEach(elements) { e in
AnyViewYouWantToShow(e)
.background(
NavigationLink("", destination: DestinationView()))
.opacity(0)
)
}
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.
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))
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())
}
}
}
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()
}
}
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")
}
2023 Update
This simple solution works for me:
ZStack {
CustomCell()
NavigationLink(destination: DetailView()) {
EmptyView()
}
.opacity(0)
}
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)
}
}
It also works with any View (not only Text)
ZStack {
Text("Some text")
NavigationLink(destination: Text("Hello")) {
EmptyView()
}.frame(width: 0)
}
I set the opacity of the navigationLink to zero and it work like a charm
NavigationLink(
destination: Text("Destination"),
label: {}
).opacity(0)