4
Xcode 11.3 (11C29).  
macOS 10.15.2.

In the SwiftUI View below there are two buttons. One prints "OK" and the other prints "Cancel". However, regardless of whichever button is pressed, both print statements are executed. Why is that? (I assume it must be a SwiftUI bug.)

struct ContentView: View {

    var body: some View {
        List {
            HStack {
                Button("OK") {
                    print("OK.")
                }
                Button("Cancel") {
                    print("Cancel")
                }
            }
        }
    }    
}

(If either the List or the HStack is commented out then each button only prints its own statement.)

Vince O'Sullivan
  • 2,403
  • 25
  • 40
  • Note: I have only ran this code on the iPhone X simulator and don't have access to my actual iPhone X until tomorrow. It is possible the problem may exist only on the simulator. – Vince O'Sullivan Dec 16 '19 at 00:26
  • 1
    For info: I have now reproduced this problem on my iPhone X. I can work around the problem easily. The real question is: Why does the problem exist? What is causing it? – Vince O'Sullivan Dec 16 '19 at 08:48
  • A year later, still a Bug with Xcode 12.3 on iOS 14.3 Device and Simulator. Happens for me even without a List but with a simple ForEach containing the Buttons. Looks like SwiftUI reuses the Button Views in a wrong way. I can see the touch highlight on both Buttons in my case. – Frederik Winkelsdorf Dec 20 '20 at 20:33

1 Answers1

2

You can use .buttonStyle(BorderlessButtonStyle()) to achieve the desired result. If you wanted to have buttons inside your List, it allow you to tap both individually as well as the List row as well.

This is an implementation with a ForEach(..) loop:

struct ContentView: View {
    var body: some View {
        List {
            ForEach(["My List Item Buttons"], id: \.self) {
                item in
                HStack {
                    Text("\(item)")
                    Spacer()
                    Button(action: { print("\(item) 1")}) {
                        Text("OK")
                    }
                    Button(action: { print("\(item) 2")}) {
                        Text("Cancel")
                    }
                }
            }
            .buttonStyle(BorderlessButtonStyle())
        }
    }
}

As a convenience, the List initialiser also allows you to use it just like the above ForEach view in case you want to have a list consisting of a single cell type only.

List(["My List Item Buttons"], id: \.self) { item in
    Text("Row \(row)")
}

OR something like this without:

struct ContentView: View {
    var body: some View {
        List {
            HStack {
                Button(action: { print("OK")}) {
                    Text("OK")
                }
                Button(action: { print("Cancel")}) {
                    Text("Cancel")
                }
            }
            .buttonStyle(BorderlessButtonStyle())
        }
    }
}
fulvio
  • 24,168
  • 20
  • 122
  • 198
  • 2
    There are many ways of working around this problem. I presented two, myself, in the question. The real question is why the problem is occurring in the first place. In your second answer above, the problem is resolved by explicitly stating the default border style. Such an action should not alter the behaviour of the buttons, yet it does. – Vince O'Sullivan Dec 16 '19 at 08:46