SwiftUI中如何实现可排序且单元格可编辑的Table?
SwiftUI中如何实现可排序且单元格可编辑的Table?
这个问题我之前在做SwiftUI可编辑排序表格的时候也踩过坑,核心就是当Table使用Binding<Row>作为元素时(也就是用Table($rows)初始化),生成的排序器是针对Binding<Row>类型的,但我们要排序的原数组是Row类型,类型不匹配导致无法直接调用sort(using:)。下面给你一个通用的解决方案,不需要依赖具体的属性KeyPath,也能完美适配多列排序的场景:
完整解决方案代码
import SwiftUI struct ContentView: View { @State var rows = [ Row(name: "A"), Row(name: "B"), Row(name: "C") ] // 注意:这里的sortOrder类型必须匹配Table要求的[KeyPathComparator<Binding<Row>>] @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) } // 如果有其他列,直接按同样方式添加即可,排序逻辑会自动适配 // TableColumn("Age", value: \.wrappedValue.age) { $row in // TextField("Enter Age", value: $row.age, format: .number) // } } .onChange(of: sortOrder) { _, newSortOrder in // 将针对Binding<Row>的排序器转换为针对Row的排序器 let rowComparators: [Comparator<Row>] = newSortOrder.map { bindingComparator in return { rowA, rowB in // 创建临时Binding适配原排序器的类型要求 let bindingA = Binding.constant(rowA) let bindingB = Binding.constant(rowB) return bindingComparator.compare(bindingA, bindingB) } } // 用转换后的排序器对原数组排序 rows.sort(using: rowComparators) } } } struct Row: Identifiable, Hashable { let id = UUID() var name: String // 可以添加更多属性 // var age: Int = 0 }
关键逻辑解释
- 保持sortOrder类型不变:因为我们用的是
Table($rows)(绑定模式的Table初始化器),所以sortOrder必须是[KeyPathComparator<Binding<Row>>]类型,不能修改这个类型,否则Table初始化会报错。 - 转换排序器类型:在
onChange(of: sortOrder)回调中,我们把每个针对Binding<Row>的KeyPathComparator转换成针对Row的Comparator:- 针对每一对
Row,我们创建临时的Binding.constant(row)来适配原排序器的类型要求; - 直接复用原排序器的
compare方法来得到排序结果,这样不管你有多少列,只要TableColumn的value参数设置正确,这个转换逻辑都能自动适配。
- 针对每一对
- 满足编辑不触发排序的需求:只有当用户点击表头切换排序顺序时,
sortOrder才会变化,进而触发排序;编辑单元格内容只会修改row的属性,不会改变sortOrder,所以不会触发重新排序,完全符合你的需求。
这个方法的好处是不需要手动维护每个列的排序逻辑,完全复用SwiftUI Table生成的排序规则,同时完美解决了类型不匹配的问题。
内容来源于stack exchange
相关产品推荐
相关产品推荐

