4

I'm using a Foreach and a DisclosureGroup to show data. Each section can Expand/Collapse. However they all are Expanding/Collapsing at the same time.

How do I Expand/Collapse each section individually?

struct TasksTabView: View {
        
    @State private var expanded: Bool = false
        
    var body: some View {
        ForEach(Array(self.dict!.keys.sorted()), id: \.self) { key in
            if let tasks = self.dict![key] {
                DisclosureGroup(isExpanded: $expanded) {
                    ForEach(Array(tasks.enumerated()), id:\.1.title) { (index, task) in
                        VStack(alignment: .leading, spacing: 40) {
                            PillForRow(index: index, task: task)
                                .padding(.bottom, 40)
                        }.onTapGesture {
                            self.selectedTask = task
                        }
                    }
                } label: {
                    Header(title: key, SubtitleText: Text(""), showTag: true, tagValue: tasks.count)
                }.accentColor(.rhinoRed)
            }
        }
    }
}
Phil Dukhov
  • 67,741
  • 15
  • 184
  • 220
John
  • 799
  • 6
  • 22

2 Answers2

11

You could have a Set containing the keys of all the expanded sections. If a section is expanded, add it to the set. It is then removed when it is collapsed.

Code:

@State private var expanded: Set<String> = []
DisclosureGroup(
    isExpanded: Binding<Bool>(
        get: { expanded.contains(key) },
        set: { isExpanding in
            if isExpanding {
                expanded.insert(key)
            } else {
                expanded.remove(key)
            }
        }
    )
) {
    /* ... */
}
George
  • 25,988
  • 10
  • 79
  • 133
0

It’s not working because the expanded flag links the DiscolureGroup all together. DisclosureGroup is smart enough to expand/collapse each item individually (see below demo).

struct ContentView: View {
    
    struct Task: Identifiable, Hashable {
        let id: UUID = UUID()
        let name: String = "Task"
    }
    
    let allTasks: [[Task]] = [
        [Task(), Task()],
        [Task()],
        [Task(), Task(), Task()]
    ]
    
    var body: some View {
        VStack {
            ForEach(allTasks.indices, id: \.self) { indice in
                DisclosureGroup() {
                    ForEach(allTasks[indice]) { task in
                        Text(task.name)
                    }
                } label: {
                    Text("Tasks \(indice)")
                }
            }
        }
    }
}

However it seems that OutlineGroup is a perfect fit to your use case:

struct Task<Value: Hashable>: Hashable {
    let value: Value
    var subTasks: [Task]? = nil
}

List(allTasks, id: \.value, children: \.subTasks) { tree in
   Text(tree.value).font(.subheadline)
}.listStyle(SidebarListStyle())
AbdelAli
  • 796
  • 1
  • 6
  • 6