如何基于自定义逻辑对SwiftUI Table的Selected列进行排序?
问题描述
我正在创建SwiftUI Table,已完成如下代码:
struct User: Identifiable { let id: Int var name: String var score: Int } struct ContentView: View { @State private var users = [ User(id: 1, name: "Taylor Swift", score: 95), User(id: 2, name: "Justin Bieber", score: 80), User(id: 3, name: "Adele Adkins", score: 85) ] @State private var sortOrder = [KeyPathComparator(\User.name)] @State private var selection: User.ID? var body: some View { Table(users, selection: $selection, sortOrder: $sortOrder) { TableColumn("Selected") { user in Toggle(isOn: viewModel.bindingForUser(user)) } TableColumn("Name", value: \.name) TableColumn("Score", value: \.score) { user in Text(String(user.score)) } } .onChange(of: sortOrder) { newOrder in users.sort(using: newOrder) } } }
新增的「Selected」列选中状态由以下ViewModel管理,User模型未定义对应属性:
class ViewModel: ObservableObject { @Published var selectedUsers: [User] = [] func bindingForUser(_ user: User) -> Binding<Bool> { .init(get: { selectedUsers.contains(user) }, set: { if $0 { selectedUsers.append(user) } else { selectedUsers.remove(user) } }) } }
现在需要实现基于「Selected」列的Table排序功能,该如何操作?
解决方案
要实现基于选中状态的排序,核心是为TableColumn配置自定义排序逻辑,同时结合ViewModel中的选中状态数据完成排序判断,具体步骤如下:
1. 修复ViewModel的数组操作问题
原ViewModel中selectedUsers.remove(user)会报错,因为数组没有直接的元素移除方法,改用基于id的过滤逻辑更可靠:
class ViewModel: ObservableObject { @Published var selectedUsers: [User] = [] func bindingForUser(_ user: User) -> Binding<Bool> { .init(get: { selectedUsers.contains { $0.id == user.id } }, set: { isSelected in if isSelected { guard !selectedUsers.contains { $0.id == user.id } else { return } selectedUsers.append(user) } else { selectedUsers.removeAll { $0.id == user.id } } }) } }
2. 为Selected列添加排序支持
给TableColumn指定自定义SortComparator,实现基于选中状态的排序规则:
TableColumn("Selected", comparator: SortComparator { lhs, rhs in // 优先按选中状态排序:选中项排在前面 let lhsSelected = viewModel.selectedUsers.contains { $0.id == lhs.id } let rhsSelected = viewModel.selectedUsers.contains { $0.id == rhs.id } if lhsSelected != rhsSelected { return lhsSelected ? .orderedAscending : .orderedDescending } // 选中状态相同时, fallback 到名称排序 return lhs.name.compare(rhs.name) }) { user in Toggle(isOn: viewModel.bindingForUser(user)) }
3. 初始化ViewModel实例
在ContentView中添加ViewModel的实例,确保视图能访问选中状态数据:
struct ContentView: View { @State private var users = [ User(id: 1, name: "Taylor Swift", score: 95), User(id: 2, name: "Justin Bieber", score: 80), User(id: 3, name: "Adele Adkins", score: 85) ] @State private var sortOrder = [KeyPathComparator(\User.name)] @State private var selection: User.ID? // 初始化ViewModel @StateObject private var viewModel = ViewModel() // 其余代码不变 }
4. 自动触发排序更新
原有的onChange(of: sortOrder)逻辑会自动处理排序:当用户点击Selected列表头时,sortOrder会包含自定义的比较器,触发users.sort(using: newOrder),应用我们定义的排序规则。
可选:支持排序方向切换
如果需要切换排序方向(比如第一次点击选中项在前,第二次未选中项在前),SwiftUI的Table会自动反转比较器的排序方向,无需额外修改代码——点击表头时,sortOrder中的比较器会自动切换order属性,排序逻辑会随之调整。
内容的提问来源于stack exchange,提问作者arun siva
相关产品推荐
相关产品推荐

