I have a simple List with nested children forming a tree. Each item can be checked. If all children are checked (or not) then the parent must reflect this state. It must also be possible to change the state of the children by selecting the parent.
Below is a simple example that sort-of works. When you change the state of a parent, the state of all the children change as well. But how do I make it work the other way around so that the parent is notified when a child changes? When all the children are checked, the parent should be checked.
I cannot get a reference to the parent. I tried adding handlers but that captures self (the parent) in an escaping closure that mutates it.
I created a timer for each item that would periodically check the children but this just feels very wrong.
I do not want to convert all the value-type coding to reference types but I cannot find an elegant way of solving this problem. Any suggestions will be greatly appreciated.
import SwiftUI
struct Item: Identifiable {
var id = UUID()
var name: String
var isSelected: Bool = false {
didSet {
guard children != nil else { return }
for i in 0..<children!.count {
children![i].isSelected = isSelected
}
}
}
var children: [Item]?
}
struct ContentView: View {
@Binding var itemsTree: [Item]
var body: some View {
List($itemsTree, children: \.children) { $item in
Toggle(item.name, isOn: $item.isSelected)
}
}
}
@main
struct ListQuestionApp: App {
@State private var itemTree: [Item] = [
Item(name: "Level 1", children: [
Item(name: "Level 2", children: [
Item(name: "Item B"),
Item(name: "Item A")
])
])
]
var body: some Scene {
WindowGroup {
ContentView(itemsTree: $itemTree)
}
}
}