Multiple Buttons in SwiftUI List Rows


In this article we would like to share what we learned about buttons in SwiftUI List rows when developing our app Cleora - HTTP and WebSocket client. We have a list in the form of a tree view: a parent row has a button to expand and collapse the children and each row also acts as a navigation link.


iPad screenshot iPad screenshot



Two Buttons in a row


Our first solution was to use Button views for both actions. A simplified example with just one row looks like this.


List {
    HStack(spacing: 20) {
        Button(action: {
            self.isExpanded.toggle()
        }) {
            Expand(isExpanded: isExpanded)
        }
        Button(action: {
            self.isSelected.toggle()
        }) {
            Select(isSelected: isSelected)
        }
    }
}


But in this senario tapping anywhere on the row will trigger both buttons. That was not the behaviour we were trying to achieve.



One View with onTapGesture modifier and one Button


To solve this we had to replace the first button with a custom view with onTapGesture modifier.


List {
    HStack(spacing: 20) {
        Expand(isExpanded: isExpanded)
            .onTapGesture {
                self.isExpanded.toggle()
            }
        Button(action: {
            self.isSelected.toggle()
        }) {
            Select(isSelected: isSelected)
        }
    }
}


In this case, tapping exactly on the Expand view will only toggle the expansion, but tapping anywhere else will trigger the Select view. This is what we wanted, as in our app the row acts as a navigation link.



Two Views with onTapGesture modifier


Another option would be to only have views with onTapGesture modifiers and no Buttons.


List {
    HStack(spacing: 20) {
        Expand(isExpanded: isExpanded)
            .onTapGesture {
                self.isExpanded.toggle()
            }
        Select(isSelected: isSelected)
            .onTapGesture {
                self.isSelected.toggle()
        }
    }
}


This way tapping exactly on the views will trigger their actions and tapping anywhere else on the row will do nothing.



Summary


When using Button views and onTapGesture modifiers in list rows in SwiftUI we can achieve various resuts depending on our needs. We have to keep in mind that if we use at least one Button the whole row will become tappable and tapping anywhere on the row will trigger all of the Buttons. Views with an onTapGesture modifier do not have the same behaviour and are only triggered when user taps exactly on their area.


You can find the full code for the examples above on GitHub.