listbuttonswiftui

Unexpected behaviour of button actions inside of a SwiftUI List


I'm facing a peculiar situation that has caused me to lose hours of work, and I'm eager to understand what's happening behind the scenes.

Here is my simplified view:

struct ToggleTextButton: View {
   var titles: [String]
   
   var body: some View {
     HStack {
       ForEach(titles, id: \.self) { title in
                      Button(action: {
                          didSelect(title)
                      }) {
                          Text(title)
                              .padding()
                              .background(.blue)
                              .foregroundColor(.white)
                              .clipShape(Capsule())
                      }
                  }
     }
   }

   func didSelect(_ title: String) {
     print("Will select:", title)
   }
 }

And here's a simple example:

List {
  ToggleTextButton(titles: ["A", "B", "C"])
  // other ToggleTextButton...
}

When pressing the button titled "A", I would expect the output to be:

Will select: A

However, here's the actual output:

Will select: A
Will select: B
Will select: C

This issue occurs only inside a List. That means if I use a ScrollView and a VStack, everything works as expected.

Can someone explain what's going on?


Solution

  • This is a limitation of SwiftUI (or maybe a feature :) ). As long as your Button style is being set to .automatic which it is by default, you will see this behavior. As soon as you add .buttonStyle(.borderless) or any other Button style, you will see the expected behavior. For reference see this question.