skip to Main Content

I have the following code:

struct ButtonTapTest: View {
    
    let items = [1, 2, 3]
    
    var body: some View {
        
        List {
            ForEach(items, id:.self) { item in
                CellTestView()
            }
        }
        
    }
}


struct CellTestView:View {
    
    
    var body: some View {
        
        VStack {
            
            Button {
                print("TOP")
            } label: {
                Image(systemName: "play.fill")
                    .font(.system(size: 40))
                    .foregroundColor(.red)
            }
            .border(.red)
            
            Spacer()
            
            Button {
                print("BOTTOM")
                
            } label: {
                Image(systemName: "play")
                    .font(.system(size: 40))
                    .foregroundColor(.red)
            }
            .border(.yellow)
            
        }
        
    }
    
}

Creates the following screen:

enter image description here

Problem:

Both button actions get triggered in the cell regardless of where I tap on the CellTestView. I want the individual button actions to trigger separately, each only when its button gets tapped, and not when I tap outside anywhere else on the cell.

You can see in the gif below that it does not matter where I tap on the CellTestView, both button actions get trigger regardless where on the view I tap, and both "TOP" and "BOTTOM" logs trigger at the same time.

How can I fix this so the two buttons in the cell receive the tap independently and only when the tap is inside the related button?

enter image description here

3

Answers


  1. My workaround for this problem is to wrap each button in its own VStack or HStack.

    Login or Signup to reply.
  2. when you have a list, tapping on a list row will trigger both buttons.
    Try this code to make each "button" equivalent, trigger separately.

    struct CellTestView: View {
    
        var body: some View {
            VStack {
                Image(systemName: "play.fill") .font(.system(size: 40)).foregroundColor(.red).border(.red)
                    .onTapGesture {
                        print("-----> playFill (playFill)")
                    }
                Spacer()
                Image(systemName: "play") .font(.system(size: 40)).foregroundColor(.red).border(.yellow)
                    .onTapGesture {
                        print("--> play (play)")
                    }
            }
        }
    }
    
    Login or Signup to reply.
  3. Whenever you have multiple buttons in a list row, you need to manually set the button style to .borderless or .plain. This is because buttons “adapt” to their context.
    According to the documentation:

    If you create a button inside a container, like a List, the style resolves to the recommended style for buttons inside that container for that specific platform.

    So when your button is in a List, its tap target extends to fill the row and you get a highlight animation. SwiftUI isn’t smart enough to stop this side effect when you have more than 2 buttons, so you need to set buttonStyle manually.

    CellTestView()
        .buttonStyle(.borderless)
    

    Result:

    Tapping top and bottom button results in separate print statements

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