Search code examples
swiftswiftuibindingtableviewtoggle

SwiftUI: Toggle in Table does not display value from model


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
    }
}

Solution

  • 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
       })
    // ...