You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于自定义逻辑对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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 01:39:40