Swiftui list row with multiple buttons?

Can one get multiple buttons to work when included in a List row?


This problem can be seen in the completed version of the Apple tutorial. There, the HikeDetail or HikeView each work when previewed by itself - one can select different graphs by the Elevation, Heart Rate, and Pace buttons. However, this view does not work properly when it is included in the ProfileSummary List(). When tapping on one of the buttons, the view collapses. This is because all buttons are activated, rather than just the one tapped.


It seems that List() is deciding that if it sees a button in the row, it will change multiple behaviors:

1) The button is no longer tinted - if one uses a simple button, it is tinted outside of a List. (Note: The tutorial is playing with colors to show blue/gray - even in the List view).

2) The button no longer flashes when pressed/tapped.

3) A tap in the row activates the actions of all buttons included in that row.


I can understand that this might be the desired behavior to make the simplest row - when only one button. But it is frustrating when one can no longer depend on the look or actions that one has built up at lower levels of the code, just because one now includes it in a List table.


One partial workaround is to not use Button(), but rather add a tap or gesture to the view one has built up. But then one doesn’t get the helpful flash animation available in a button.


Looking particularly for a way to get items 2 and 3 above to work together. Can work around item 1 like was done in the tutorial - but might be nice if this worked, too.

Post not yet marked as solved Up vote post of anorskog Down vote post of anorskog
24k views

Replies

Note: Just updated to XCode 11.0 beta 3 and a new copy of the Apple Tutorial. The Tutorial still shows the problem.

When I put multiple buttons in the same List or Form, initially all the buttons provide only the action of the first button. Thereafter, additional taps produce no action. I am doing this in the context of “help” buttons that open an alert sheet with guidance for using a particular part of the app.

Same problem with me in Xcode 11 beta 3. I tried to nest three buttons in a stack ,then put the stack in a row of a list. As the code shows:

struct TestCellTap : View {
    var body: some View {
        List {
            SomeButtons()
        }
    }
}

struct SomeButtons: View {
    var body: some View {
        HStack {
            Button(action: {
                print("First button is tapped")
            }) {
                Text("First")
            }
            Spacer()
            Button(action: {
                print("Second button is tapped")
            }) {
                Text("Second")
            }
            Spacer()
            Button(action: {
                print("Third button is tapped")
            }) {
                Text("Third")
            }
        }
    }
}


When I tried to tap one of the three buttons, it seems that the row got the tap instead of any button it nests, and all the actions of the three buttons were triggered. And I got same result when tapping at any point out of the three buttons.

Upgraded to beta 4. The tutorial is still broken and can't deal with multiple buttons on the "Recent Hikes" row. And as shown by the simpler example above by SilentJohn.

Upgraded to beta 5. The Apple tutorial is still broken - "Recent Hikes" row still doesn't behave as it should. Basically, still indicating that you still cannot have multiple buttons on a List row. Even if the standard was to only allow/work with one button, I was hoping that Apple would provide a way to turn off the "standard" behavior so that the behavior that was built up for that item (as seen earlier in the tutorial) would work when it was included in a List.

I'm having the same issue. I was wondering if we can simply tell SwiftUI to not do its default behavior.


I experimented with .lineStyle(nil), but it didn't work.


Does anyone know a way to tell SwiftUI to ignore its default behavior (namely, the 3 points mentioned in the first post)?

Upgraded to beta 6. Problem described in this post and the Apple tutorial are still broken as described above.

Broken record. Apple tutorial still not working with Beta 7.

Try leaving the action blank and use .onTapGesture instead:


                    Button(action: {}, label: {Image(systemName: "chevron.right.circle")}).onTapGesture {
                        <#code#>
                    }
  • thank you. After hours of fruitless experiment this was the answer. Why Apple should think that clicking anywhere is a view (that is a row in a List) should call the action ALL buttons anywhere in that view is beyond me

  • thanks a lot. the problem still exists, and your answer still works :)

  • This is still the correct answer. Thank you!

Add a Comment

Thanks. One can sort of get things to work if one moves the action for ALL the buttons to their respective onTapGesture{} call, and not just the chevron button.


But then one realizes that one doesn't need (shouldn't use?) buttons at all! Removing the action for a button, then one should just use the Image() or Text() "views" with the onTapGesture{} call.


So, basically, if you are going to eventually use List(), don't use Button() - especially if you might have multiple in a cell. ??? Guess I'm still hoping Apple will eventually allow multiple buttons in a cell.

I just spent hours trying to figure out why my buttons were not working and tracked down this being the reason. If you use a scrollview instead they work. However scrollview currently has its own problems when being used with a navigation link.

Apple Tutorial still broken after upgrading to Xcode GM_Seed. Still has no change or workaround for dealing with multiple buttons in a List row.

I imagine they (Apple) do not care that much about updating the tutorials. Once broken, they should be considered lost !

Here one solution using a modifier and ignoring the button tap. ButtonRefresh below is in a list.


Visually the containing list item "clicks", but only the referesh button is actioned.


Hope it helps



struct YastIconButtonStyle: ViewModifier {

func body(content: Content) -> some View {

content

.background(Color.yellow)

.mask(Circle())

}

}




extension View {

func ApplyImageIconsButtonStyle(command: @escaping () -> Void) -> some View {

self

.modifier(YastIconButtonStyle())

.onTapGesture {

command()

}

}

}



struct ButtonRefresh: View {

var body: some View {

Button(action: CmdLoadPortolioOnline) {

Image("Refresh")

.ApplyImageIconsButtonStyle(command: CmdLoadPortolioOnline)

}.onTapGesture {

// Do nothing

}

}

}

Setting the ButtonStyle seems to do the trick

struct Test: View {
  var body: some View {
    NavigationView {
      List {
        Button(action: { print("Prefix")}) {
          Text("Prefix 1")
        }
        Button(action: { print("Prefix")}) {
          Text("Prefix 2")
        }
        .buttonStyle(DefaultButtonStyle())
        ForEach([
          "Line 1",
          "Line 2",
        ], id: \.self) {
          item in
          HStack {
            Text("\(item)")
            Spacer()
            Button(action: { print("\(item) 1")}) {
              Text("Button 1")
            }
            Button(action: { print("\(item) 2")}) {
              Text("Button 2")
            }
          }
        }
        .onDelete { _ in }
        .buttonStyle(BorderlessButtonStyle())
      }
      .navigationBarItems(trailing: EditButton())
    }
    .accentColor(.red)
  }
}