I want to display a Table with Toggles in it on the Mac in Xcode 14. The following code creates the table correctly, but the toggle does not display the bool value it should display. It is always turned off. The toggle actually toggles the value of the items. It just does not display the state. I have no idea, what I am doing wrong. Thanks for any help in advance...
import SwiftUI
@main
struct DataGridViewApp: App {
let tableViewProvider = TableViewProvider()
var body: some Scene {
WindowGroup {
ContentView(viewProvider: tableViewProvider)
.onAppear() { tableViewProvider.addElements() }
}
}
}
struct ContentView: View {
@ObservedObject var viewProvider: TableViewProvider
var body: some View {
Table(viewProvider.tableViewDataList) {
TableColumn("Value") { item in
Toggle("", isOn: Binding<Bool>(
get: {
print("-\nget \(viewProvider.tableViewDataList.first(where: { $0.id == item.id })?.value ?? false)")
return viewProvider.tableViewDataList.first(where: { $0.id == item.id })?.value ?? false
//return item.value
},
set: {
print("set \($0)")
viewProvider.tableViewDataList.first(where: { $0.id == item.id })?.value = $0
print("after set \(viewProvider.tableViewDataList.first(where: { $0.id == item.id })?.value ?? false)")
//item.value = $0
})
)
}
TableColumn("ID") { item in
Text(String(item.id))
}
}
}
}
class TableViewProvider: ObservableObject {
@Published var tableViewDataList: [Item] = Array()
func addElements() {
tableViewDataList.append(Item(id: 1))
tableViewDataList.append(Item(id: 2))
tableViewDataList.append(Item(id: 3))
}
}
class Item: ObservableObject, Identifiable {
var id: Int
@Published var value: Bool = false
init(id: Int) {
self.id = id
}
}
CodePudding user response:
ContentView is observing TableViewProvider and changing a value in Item does not notify the View to update since it's nested in an Array within your provider. One approach in you code could be adding viewProvider.objectWillChange.send()
in your setter after viewProvider.tableViewDataList.first(where: { $0.id == item.id })?.value = $0
. This would notify the view that something changed
// ...
set: {
print("set \($0)")
viewProvider.tableViewDataList.first(where: { $0.id == item.id })?.value = $0
viewProvider.objectWillChange.send() // Manually notify that viewProvider data has changed
print("after set \(viewProvider.tableViewDataList.first(where: { $0.id == item.id })?.value ?? false)")
print(viewProvider.tableViewDataList)
//item.value = $0
})
// ...
CodePudding user response:
do not nest your ObservableObject
, like you do, it creates problems.
Make Item
a struct, such as:
// -- here
struct Item: Identifiable {
var id: Int
var value: Bool = false
}
struct ContentView: View {
@ObservedObject var viewProvider: TableViewProvider
var body: some View {
Table(viewProvider.tableViewDataList) {
TableColumn("Value") { item in
Toggle("", isOn: Binding<Bool>(
get: {
print("-\nget \(viewProvider.tableViewDataList.first(where: { $0.id == item.id })?.value ?? false)")
return viewProvider.tableViewDataList.first(where: { $0.id == item.id })?.value ?? false
//return item.value
},
set: {
print("set \($0) item: \(item.id)") // <-- here
// -- here
if let ndx = viewProvider.tableViewDataList.firstIndex(where: { $0.id == item.id }) {
viewProvider.tableViewDataList[ndx].value = $0
}
print("after set \(viewProvider.tableViewDataList.first(where: { $0.id == item.id })?.value ?? false)")
//item.value = $0
})
)
}
TableColumn("ID") { item in
Text(String(item.id))
}
}
}
}
class TableViewProvider: ObservableObject {
@Published var tableViewDataList: [Item] = [] // <-- here
func addElements() {
tableViewDataList.append(Item(id: 1))
tableViewDataList.append(Item(id: 2))
tableViewDataList.append(Item(id: 3))
}
}