I want to create a sortable Table
where each table cell is a TextField
so that the cell contents can be edited.
Let's start with this unsortable Table
that has only one column. In the real code, this is of course going to have more columns and rows can be added and removed too.
struct ContentView: View {
@State var rows = [
Row(name: "A"),
Row(name: "B"),
]
var body: some View {
Table($rows) {
TableColumn("Name") { $row in
TextField("Enter Name", text: $row.name)
}
}
}
}
struct Row: Identifiable, Hashable {
let id = UUID()
var name: String
}
To make this sortable, I would need to pass a sortOrder:
binding to Table
, as well as well as manually sorting the rows
array in onChange(of: sortOrder) { ... }
.
However, this doesn't work:
@State var sortOrder: [KeyPathComparator<Binding<Row>>] = []
var body: some View {
Table($rows, sortOrder: $sortOrder) {
TableColumn("Name", value: \.wrappedValue.name) { $row in
TextField("Enter Name", text: $row.name)
}
}
.onChange(of: sortOrder) { _, sortOrder in
rows.sort(using: sortOrder) // error
}
}
Instance method
sort(using:)
requires the typesRow
andBinding<Row>
be equivalent
I understand that this is because I am trying to sort an array of Rows
using sort comparators that compare Binding<Row>
s.
However, the Table
initialiser requires that the type of sortOrder:
to be
Binding<[KeyPathComparator<Binding<Row>>]>
because this is a table of Binding<Row>
s.
How can I sort the rows in onChange
? Perhaps there is a way to convert [KeyPathComparator<Binding<Row>>]
to [KeyPathComparator<Row>]
?
Just in case this is relevant, I only want the table to be re-sorted when sortOrder
changes. I do not want the table to be re-sorted when some row's name
is edited.
You could try this approach using a dedicated Binding
in the TextField
,
rather than having a $rows
in the Table
.
Example code:
struct ContentView: View {
@State private var rows = [Row(name: "A"), Row(name: "B")]
@State private var sortOrder = [KeyPathComparator(\Row.name)] // <-- here
var body: some View {
Table(rows, sortOrder: $sortOrder) { // <-- here
TableColumn("Name", value: \Row.name) { row in
TextField("Enter Name", text: Binding( // <-- here
get: { row.name },
set: {
if let index = rows.firstIndex(of: row) {
rows[index].name = $0 // <-- here
}
}))
}
}
.onChange(of: sortOrder) {
rows.sort(using: sortOrder)
}
}
}
struct Row: Identifiable, Hashable {
let id = UUID()
var name: String
}