TextEditor cells in SwiftUI Table

I need to have a proper, multi-line text editor in a table (SwiftUI Table).

Requirement is not a typical spreadsheet, it is rather a table with cells of larger size, mostly for editing multi-line textual content. Expectation is a dozen or two of columns and potentially many rows. Some columns might contain single simple scalar values, those might be using for sorting, for example.

Here is a simple (wrong) code snippet that demonstrates what I am trying to achieve at this moment:

struct Thing: Identifiable {
    var id: Int
    var text: String

    init(id: Int, text: String="") {
        self.id = id
        self.text = text
    }
}

var data = [
    Thing(id: 1, text: "one"),
    Thing(id: 2, text: "two"),
    Thing(id: 3, text: "three"),
]

struct ContentView: View {
    var body: some View {
        VStack {
            Text("Hello, world!")
                .padding()
            Table(data) {
                TableColumn("ID") { row in Text(String(row.id)) }
                // Error: Cannot convert value of type 'String' to expected argument type 'Binding<String>'
                TableColumn("Text") { row in TextEditor(text: row.text) }
            }
        }
    }
}

Question: What is the proper way of defining that the row.text is a binding in this case?

Side note: I can not use key paths, since at the end the Thing will also be some dynamic structure not known at compile time - the structure will be editable by the user.

Answered by OOPer in 692834022

What is the proper way of defining that the row.text is a binding in this case?

As far as I read the docs and watched the sample code, SwiftUI.Table does not give us a quick way to make some cell editable.

One possible solution would be something like this:

(Not sure, if this code would work as expected.)

struct ContentView: View {

    @State var data = [
        Thing(id: 1, text: "one"),
        Thing(id: 2, text: "two"),
        Thing(id: 3, text: "three"),
    ]
    
    var body: some View {
        VStack {
            Text("Hello, world!")
                .padding()
            Table(data) {
                TableColumn("ID") { row in Text(String(row.id)) }
                TableColumn("Text") { row in
                    //`id` needs to keep the position in `data`
                    TextEditor(text: $data[row.id-1].text)
                }
            }
        }
    }
}

at the end the Thing will also be some dynamic structure not known at compile time

That sounds like you should better stay in the AppKit world a little more, until SwiftUI gives us more convenient ways to write some dynamic things.

Accepted Answer

What is the proper way of defining that the row.text is a binding in this case?

As far as I read the docs and watched the sample code, SwiftUI.Table does not give us a quick way to make some cell editable.

One possible solution would be something like this:

(Not sure, if this code would work as expected.)

struct ContentView: View {

    @State var data = [
        Thing(id: 1, text: "one"),
        Thing(id: 2, text: "two"),
        Thing(id: 3, text: "three"),
    ]
    
    var body: some View {
        VStack {
            Text("Hello, world!")
                .padding()
            Table(data) {
                TableColumn("ID") { row in Text(String(row.id)) }
                TableColumn("Text") { row in
                    //`id` needs to keep the position in `data`
                    TextEditor(text: $data[row.id-1].text)
                }
            }
        }
    }
}

at the end the Thing will also be some dynamic structure not known at compile time

That sounds like you should better stay in the AppKit world a little more, until SwiftUI gives us more convenient ways to write some dynamic things.

Thank you. That solution technically worked, although it seems that it would require some workarounds to make it practically useable. Looks like I will have to rethink how I approach the problem for now.

TextEditor cells in SwiftUI Table
 
 
Q