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
相关产品推荐
相关产品推荐

