Question

How do you implement list selection with children in SwiftUI?

Answer and Explanation

Implementing list selection with children in SwiftUI involves creating a data structure that represents the hierarchical relationship between parent and child items, and then using the List and DisclosureGroup views to display the data.

Here's a breakdown of the process:

1. Data Structure:

- First, define a struct or class that represents a single item in your list, which may have children. This struct should conform to Identifiable protocol so that SwiftUI can properly identify each list row.

- Create a recursive data structure that can hold parent and child data. This will usually include an array of child items within the parent item.

2. SwiftUI List and DisclosureGroup:

- Use the List view to display all items and DisclosureGroup to show expandable/collapsible parts for parent-child relations.

- The DisclosureGroup will be used inside the list row, which when expanded will show its child items.

3. Selection Logic:

- Store the selected items using the @State or @ObservedObject property wrapper. Use either single or multiple selections depending on what is needed for your implementation.

- Handle user tap on items, update the selected list and refresh the view.

4. Example Implementation:

Here’s a simplified code example demonstrating this concept: import SwiftUI

struct ListItem: Identifiable {
  let id = UUID()
  let name: String
  var children: [ListItem]? = nil
}

struct ContentView: View {
  @State private var selectedItems = Set()
  let items: [ListItem] = [
    ListItem(name: "Parent 1", children: [
      ListItem(name: "Child 1.1"),
      ListItem(name: "Child 1.2")
    ]),
    ListItem(name: "Parent 2", children: [
      ListItem(name: "Child 2.1"),
      ListItem(name: "Child 2.2", children: [
        ListItem(name: "Child 2.2.1")
      ])
    ]),
    ListItem(name: "Parent 3")
  ]

  var body: some View {
    List {
      ForEach(items) { item in
        ListItemView(item: item, selectedItems: $selectedItems)
      }
    }
  }
}

struct ListItemView: View {
  let item: ListItem
  @Binding var selectedItems: Set
  @State private var isExpanded: Bool = false

  var body: some View {
    VStack(alignment: .leading) {
      HStack {
        Image(systemName: selectedItems.contains(item.id) ? "checkmark.circle.fill" : "circle")
          .onTapGesture {
            if selectedItems.contains(item.id) {
              selectedItems.remove(item.id)
            } else {
              selectedItems.insert(item.id)
            }
        }
        Text(item.name)
      }
      if let children = item.children {
        DisclosureGroup(isExpanded: $isExpanded){
          ForEach(children) { child in
            ListItemView(item: child, selectedItems: $selectedItems).padding(.leading, 20)
          }
        } label: {
          EmptyView()
        }
      }
    }
  }
}

#Preview {
  ContentView()
}

This implementation uses DisclosureGroup to allow the parent to expand and reveal children and onTapGesture to toggle selection. Note that this is a basic example and might need adjustments depending on the specific selection requirements.

By using these techniques, you can create interactive lists with hierarchical data, providing a more intuitive user experience.

More questions