skip to Main Content

I have a List and in each row I put a button at the left end and a text label on the right next to it. I want this button to have an action and the entire row to also have a tap gesture. So how can I properly distinguish between the gestures so that when the use taps the button it does the action of the button, and when the user tap anywhere else in the row, it does the tap gesture of the row?

Here is my code for now:

List {
    ForEach(items) { item in
        HStack {
            Button {
                buttonAction()
            } label: {
                Image(systemName: "circle.fill")
            }
            Text("Row Title")
        }
        .onTapGesture {
            rowAction()
        }
    }
}

Now, when I tap the button or the text it does the rowAction() , whereas when I tap other places in the row it does the buttonAction().

2

Answers


  1. This should do it:

     HStack {
            Button {
                //buttonAction()
            } label: {
                Image(systemName: "circle.fill")
            }
            
            HStack {
                Button {
                    print("b2 tapped")
                } label: {
                    Text("Row Title")
                        .frame(maxWidth: .infinity)
                }
            }
            .padding()
            .background(Color.green.opacity(0.3), in: RoundedRectangle(cornerRadius: 10))
            
        }
        .padding()
    }
    
    Login or Signup to reply.
  2. If you add a .border(.blue) to your HStack, you’ll notice that it doesn’t quite cover the entire row – and is therefore not tappable.

    Strangely, if you add a .border(.red) to your Button, you’ll notice that it also doesn’t cover the entire row, but the tappable area of the button expands beyond the size of its elements (so the red border doesn’t actually show the tappable button area).

    While at it, add a green border to the Text to better visualize everything: Text("Row Title").border(.green)

    To fix everything:

    1. Limit the tappable area of the button by changing the button style to borderless:
    Button {
        buttonAction()
    } label: {
        Image(systemName: "circle.fill")
    }
    .buttonStyle(.borderless)
    
    1. Expand the overall size of the HStack to cover the entire row, by simply adding a spacer:
    Text("Row Title")
       .border(.green)
                        
    Spacer()
    

    With the spacer added, you will see the expanded area of the tappable HStack, shown by the blue border.

    The entire code:

    List {
        ForEach(items) { item in
            HStack {
                Button {
                    buttonAction()
                } label: {
                    Image(systemName: "circle.fill")
                }
                .border(.red)
                .buttonStyle(.borderless) // <-- This limits the tappable area of the button to just the image/circle.
    
                Text("Row Title")
                .border(.green)
    
                Spacer() // <-- This increases the overall size of the horizontal stack
            }
            .border(.blue)
            .onTapGesture {
                rowAction()
            }
        }
    }
    

    Note: I left the borders in there for visualization purposes, but they’re not part of the solution. Remove them at will.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search