Why buttons don't work when embedded in a SwiftUI Form?

From what I've been able to tell, Form rows behave differently when they contain a View that accepts a tap gesture, such as a button. When the row contains a Button, then when the user taps on that button, everything else in the same row also receives a tap gesture. So in your example above, it's not that your buttons are not working, what is actually happening is that they are both receiving a tap gesture and executing their action, but because of the logic in your code effectively cancels the effect of the other, it appears as if they are not working.

To help demonstrate this point, here is some test code I wrote:

    @State var count1 = 0
    @State var count2 = 0

    func buttonTest() -> some View {
        HStack {
            Button(action: {
                self.count1 += 1
            }) {
                Text("Button 1: \(count1)")
            }
            Spacer()
            Button(action: {
                self.count2 += 1
            }) {
                Text("Button 2: \(count2)")
            }
        }
    }

    var body: some View {
        VStack {
            buttonTest()
            Form {
                Section(header: Text("Test")) {
                    buttonTest()
                }
            }
        }
    }

If you tap either button above the form, they work independently as expected, incrementing only their own count, but if you tap either button in the Form, or anywhere else in the row, then both buttons' actions execute and both counts increment.

I'm not sure why Apple do this, but I too am struggling to find a solution to this problem.

EDIT

Thanks to Shauket Sheikh, there is a solution, but it makes code re-use less elegant.

So when the Button is in a Form, you need to put the code into an .onTapGesture handler, but if the Button is outside a Form, you need the code in the action: handler.

I really hope this isn't Apple's long-term position on Forms: it's goes against the write once, use everywhere philosophy.


Try adding a buttonStyle modifier to the buttons in your Form. This worked for me:

Button(action: {}, label: {Text("Test")})
    ­­­­­.buttonStyle(PlainButtonStyle())

Tags:

Swiftui