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

SwiftUI带复选框列的Table排序后复选框无法对应正确条目

SwiftUI带复选框列的Table排序后复选框无法对应正确条目

嘿,我来帮你理清这个问题~

首先咱们得搞明白为啥用索引当ID会出问题:你把Person的id设成了数组的索引,当你对数组排序后,元素的位置变了,但每个Person的id还是原来的索引值。这时候你用$people[person.id].checkedIn绑定复选框,就会指向数组里原来索引位置的元素,而不是当前显示的这个Person,自然就出现复选框和对应条目不匹配的情况了。

正确的做法是用UUID作为Identifiable的id,因为UUID是每个对象独有的、不会改变的标识,不管数组怎么排序、增删元素,每个Person的id都能精准对应到自己。

接下来咱们一步步修改代码:

1. 修正Person结构体的ID类型

把id改成UUID,并且用let修饰保证它不会被修改:

struct Person: Identifiable {
    let id: UUID // 唯一且不变的标识
    var name: String
    var checkedIn: Bool
}

2. 初始化数组时生成UUID

给每个Person实例生成独立的UUID:

@State var people = [
    Person(id: UUID(), name: "George", checkedIn: false),
    Person(id: UUID(), name: "Leah", checkedIn: true),
    Person(id: UUID(), name: "Sarah", checkedIn: false)
]

3. 修复复选框的绑定逻辑

现在不能再用id当索引了,咱们需要通过Person的UUID找到它在数组中的实际位置,再绑定对应的checkedIn属性:

Toggle("Checked In", isOn: $people[people.firstIndex(where: { $0.id == person.id })!].checkedIn)
    .labelsHidden()

这里用firstIndex(where:)找到当前person在数组中的索引,因为Table的元素都是来自people数组,所以这个索引肯定存在,强制解包是安全的。

4. 其他部分的调整

原来的selected集合是Set<Person.ID>,现在Person.ID是UUID类型,这个声明不需要改,Swift会自动推断类型。排序逻辑也不用动,你的BoolComparator实现是没问题的。

完整的修改后代码

import SwiftUI

struct ContentView: View {
    struct Person: Identifiable {
        let id: UUID
        var name: String
        var checkedIn: Bool
    }
    
    @State var people = [
        Person(id: UUID(), name: "George", checkedIn: false),
        Person(id: UUID(), name: "Leah", checkedIn: true),
        Person(id: UUID(), name: "Sarah", checkedIn: false)
    ]
    
    @State private var selected = Set<Person.ID>()
    @State private var sortOrder = [KeyPathComparator(\Person.name)]
    
    private struct BoolComparator: SortComparator {
        typealias Compared = Bool
        
        func compare(_ lhs: Bool, _ rhs: Bool) -> ComparisonResult {
            switch (lhs, rhs) {
            case (true, false):
                return order == .forward ? .orderedDescending : .orderedAscending
            case (false, true):
                return order == .forward ? .orderedAscending : .orderedDescending
            default: return .orderedSame
            }
        }
        
        var order: SortOrder = .forward
    }
    
    var body: some View {
        Table(people, selection: $selected, sortOrder: $sortOrder) {
            TableColumn("Name", value: \.name)
            TableColumn("Checked In", value: \.checkedIn, comparator: BoolComparator()) { person in
                Toggle("Checked In", isOn: $people[people.firstIndex(where: { $0.id == person.id })!].checkedIn)
                    .labelsHidden()
            }
        }
        .onChange(of: sortOrder) { newOrder in
            people.sort(using: newOrder)
        }
        .padding()
    }
}

这样修改后,不管怎么排序数组,每个复选框都会精准绑定到对应的Person的checkedIn属性,不会再出现错位的情况啦。

备注:内容来源于stack exchange,提问作者staplegun

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.22 07:59:34