skip to Main Content

I’m working on a SwiftUI app where I have a List that needs to support both selection of items and handling double tap gestures on those items. However, I’m facing an issue where enabling onTapGesture seems to interfere with the list’s selection functionality. When the tap gesture is enabled, the selection doesn’t work as expected. Disabling onTapGesture makes the selection work again.

Here is a minimal example of my code:

import SwiftUI

struct ContentView: View {
    @State private var selectedItemId: UUID?
    @State private var items: [ListItem] = ListItem.sampleItems

    var body: some View {
        List(selection: $selectedItemId) {
            ForEach(items, id: .id) { item in
                Text(item.name)
                    .onTapGesture {
                        // Handle tap gesture here
                        print("Item tapped: (item.name)")
                    }
                    .tag(item.id) // This is important for selection to work
            }
        }
        .onAppear {
            // Optional: Perform any initial setup
        }
    }
}

struct ListItem: Identifiable {
    let id: UUID
    let name: String

    static var sampleItems: [ListItem] {
        [
            ListItem(id: UUID(), name: "Item 1"),
            ListItem(id: UUID(), name: "Item 2"),
            ListItem(id: UUID(), name: "Item 3")
        ]
    }
}

@main
struct MyApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

I’m looking for suggestions on how to resolve this issue. Ideally, I would like the list to support both item selection and handling tap gestures without interfering with each other. Has anyone encountered a similar issue or have any recommendations on how to approach this?

Thank you in advance for your help!

2

Answers


  1. Assuming you want the list to also change selection when onTapGesture is triggered, you can simply programmatically "select" the item in the onTapGesture closure:

    Text(item.name)
        .onTapGesture {
            selectedItemId = item.id // selects the tapped item
            print("Item tapped: (item.name)")
        }
        .tag(item.id)
    
    Login or Signup to reply.
  2. Manually selecting the cell most probably should solve your problem (as described in @Sweeper’s answer). Although you might need to change your row content (in your case the Text) so it fills the whole row.

    As an alternative approach you can subscribe to the selectedItemId by using Combine.

    import SwiftUI
    import Combine
    
    struct ContentView: View {
        @State private var selectedItemId: UUID?
        @State private var items: [ListItem] = ListItem.sampleItems
    
        var body: some View {
            List(selection: $selectedItemId) {
                ForEach(items, id: .id) { item in
                    Text(item.name)
                        .tag(item.id) // This is important for selection to work
                }
            }
            .onReceive(Just($selectedItemId)) { _ in
                if let selectedItemId {
                    print("item is selected with id: (selectedItemId)")
                }
            }
        }
    }
    

    So every time the user selects a cell the onReceive block will be called (even when the user taps the same cell multiple times).

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search