Swiftui How can you use on Tap Gesture for entire row in a foreach loop
Asked Answered
D

2

17

I am creating a SearchBar right now that gets data from API, the Text highlighted in red ( see image below) is what I want to pass into my userDefault string on tap . This is working as long as I tap the highlighted red text, is it possible to get that value when you tap anywhere in that row like say the white space ? any suggestions would be great .

@State var model: [SearchModel]
var defaults = UserDefaults.standard
@State var isOpen: Bool = false

Above is my State variables and my user default . I won't show the whole code since the issue is only with the below . As you can see my values are in the foreach I just want to have it so that if a user clicks on the white space of the row that I can still execute this code .

                     .onTapGesture {
                                      isOpen = true
                                     // I set the value here
                                    defaults.setValue(value.name, forKey: "location") 
                                }.fullScreenCover(isPresented: $isOpen, content: MainView.init)

The code above gives me the correct value but it's tied to the Text

       ZStack {
                Color(UIColor.white)
                    .ignoresSafeArea()
                ScrollView(showsIndicators: false) {
                    LazyVStack {
                        ForEach(model) { value in
                            VStack(alignment: .leading) {
                                HStack {
                                    Text(value.name)
                                        .font(.system(size: 15))
                                        .background(Color.red)
                                        .padding(.trailing, 10.0)
                                        .onTapGesture {
                                            isOpen = true
                        // I set the value here
                                            defaults.setValue(value.name, forKey: "location") 
                                        }.fullScreenCover(isPresented: $isOpen, content: MainView.init)
                          
                                }
                                HStack {
                                    Text(value.statelong)
                                        .foregroundColor(Color.gray)
                                        .font(.system(size: 13))
                                    Spacer()
                                }
                                
                            }
                            VStack {
                                Spacer()
                                Divider()
                            }
                            
                        }
                        
                        
                    }.padding(.leading, 20.0)
                    
                    
                }
                
            }
            .listRowInsets(.none)

enter image description here

Dotterel answered 1/12, 2020 at 23:27 Comment(0)
D
25

It only works for the highlighted text because the onTapGesture is on the Text view.

1. Using a HStack()

I would embed the VStack in a HStack and add a Spacer() to fill the entire area, then place the onTapGesture to the HStack, like this:

    ForEach(model) { value in
                HStack{
                    VStack(alignment: .leading) {
                        HStack {
                            Text(value.name)
                                .font(.system(size: 15))
                                .background(Color.red)
                                .padding(.trailing, 10.0)
                        }
                        HStack {
                            Text(value.statelong)
                                .foregroundColor(Color.gray)
                                .font(.system(size: 13))
                            Spacer()
                        }
                        
                    }
                    Spacer()
                }.contentShape(Rectangle())
                .onTapGesture {
                    isOpen = true
                    defaults.setValue(value.name, forKey: "location")
                }.fullScreenCover(isPresented: $isOpen, content: MainView.init)
            }

2. Using a ZStack()

Or you could use a ZStack to make a full view background and place the onTapGesture on the ZStack, add .frame(maxWidth: .infinity) to the ZStack to fill the entire available width, like this:

 ForEach(model) { value in
                ZStack {
                    VStack(alignment: .leading) {
                        HStack {
                            Text(value.name)
                                .font(.system(size: 15))
                                .background(Color.red)
                                .padding(.trailing, 10.0)
                        }
                        HStack {
                            Text(value.statelong)
                                .foregroundColor(Color.gray)
                                .font(.system(size: 13))
                            Spacer()
                        }
                        
                    }
                    Spacer()
                }.contentShape(Rectangle())
                .frame(maxWidth: .infinity)
                .onTapGesture {
                    isOpen = true
                    defaults.setValue(value.name, forKey: "location")
                }.fullScreenCover(isPresented: $isOpen, content: MainView.init)
            }
Dagan answered 1/12, 2020 at 23:42 Comment(2)
You probably need to attach .contentShape(Rectangle()) to HStack/VStack before .onTapGesture.Rosati
The Spacer() in the first solution does the trick!Alliance
U
19

Just add .contentShape(Rectangle()) ViewModifier to your HStack, and nothing else

Underproof answered 19/1, 2023 at 8:26 Comment(1)
omg thank you so much for this, you saved me! Every button, slider, view, etc across my app had to have the text clicked on the trigger the onTapGesture and I couldn't for the life of me figure it out. Do you know why we have to do this? Does SwiftUI automatically add a contentShape (clickable area I assume?) to text inside views if not explicitly specified?Shala

© 2022 - 2024 — McMap. All rights reserved.