Search code examples
iosswiftswiftuiswiftui-listswiftui-navigationlink

On tap anyplace in List row the navigation link is triggered, have some way to change this behavior?


I'm creating a list using Scrollview + LazyVStack in iOS 14 and List in iOS 13, but the tap behavior is different. If I put a NavigationLink inside the LazyVStack the link only is triggered when tapped, but in List if I tap anyplace in the row the NavigationLink is triggered.

Have a way to trigger some NavigationLink inside a List only when tap in there? This code bellow is an example, the real project has a lot of components inside components, it's not simply put the navigation link outside the list.

Code:

List screen

import SwiftUI

struct ContentView: View {
    
    @State var navigate = false
    
    var body: some View {
        NavigationView {
            VStack {
                VList {
                    ForEach(0..<3) { index in
                        VStack(alignment: .leading) {
                            HStack {
                                Text("Item title")
                                    .font(.system(size: 17))
                                    .fontWeight(.semibold)
                                    .frame(idealWidth: .infinity, maxWidth: .infinity, alignment: .leading)
                                    .padding(.all, 4)
                                
                                ZStack {
                                    Text("See more")
                                        .font(.system(size: 15))
                                        .underline()
                                        .padding(.all, 4)
                                        .onTapGesture {
                                            self.navigate = true
                                        }
                                    
                                    NavigationLink(
                                        destination: Text("Destination"),
                                        isActive: $navigate,
                                        label: {
                                            EmptyView()
                                        })
                                        .buttonStyle(PlainButtonStyle())
                                }
                            }
                            
                            Text("Description of item")
                        }
                    }
                }
            }
            .navigationBarHidden(true)
            .navigationBarTitle("")
        }
    }
}

VList component

import SwiftUI

struct VList<Content:View>: View {
    
    var content: () -> Content
    
    var body: some View {
        if #available(iOS 14.0, *) {
            ScrollView {
                LazyVStack(spacing: 0) {
                    content()
                }
            }
        } else {
            List {
                content()
                    .padding(.horizontal, -20)
            }
            .onAppear {
                UITableView.appearance().separatorStyle = .none
                UITableViewCell.appearance().selectionStyle = .none
            }
        }
    }
}

Solution

  • You have to give a background to VStack and also using onTapGesture. So I do not know what is your app Color or background I gave invisible Color, you can replace it with your Color. Also note that you should tell to SwiftUI that background is in used and it makes that SwiftUI does not apply actions to all VStack.


    struct ContentView: View {
        
        @State var navigate = false
        
        var body: some View {
            NavigationView {
                VStack {
                    List {
                        ForEach(0..<3) { index in
                            VStack(alignment: .leading) {
                                HStack {
                                    Text("Item title")
                                        .font(.system(size: 17))
                                        .fontWeight(.semibold)
                                        .frame(idealWidth: .infinity, maxWidth: .infinity, alignment: .leading)
                                        .padding(.all, 4)
                                    
                                    ZStack {
                                        Text("See more")
                                            .font(.system(size: 15))
                                            .underline()
                                            .padding(.all, 4)
                                            .onTapGesture {
                                                self.navigate = true
                                            }
                                        
                                        NavigationLink(
                                            destination: Text("Destination"),
                                            isActive: $navigate,
                                            label: {
                                                EmptyView()
                                            })
                                            .buttonStyle(PlainButtonStyle())
                                    }
                                }
                                
                                
                                Text("Description of item")
                            }
                            .background(Color.white.opacity(0.01).onTapGesture { })    // << : Here
                        }
                    }
                }
                .navigationBarHidden(true)
                .navigationBarTitle("")
            }
        }
    }