Home > Back-end >  SwiftUI: Toggle in Table does not display value from model
SwiftUI: Toggle in Table does not display value from model

Time:10-14

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))
    }
}
  • Related