SwiftUI报错:Generic struct 'ForEach'要求'Set<Exercise>'遵循RandomAccessCollection
解决SwiftUI中ForEach使用Set报错的问题
错误原因
没错,问题就是出在用了Set而不是数组上。SwiftUI的ForEach要求数据源必须遵循RandomAccessCollection协议,而Set是无序集合,不符合这个要求,所以直接传Set就会触发报错。
修复方案
方案一:临时把Set转成数组
不需要修改存储类型,只需要在传给ForEach的时候把Set转换成数组就行。不过要注意Set是无序的,转成数组后元素顺序不固定,如果需要固定顺序,得额外做排序处理。
修改后的关键代码:
// 把原来的ForEach(selectedItems)改成下面这样 ForEach(Array(selectedItems)) { e in NavigationLink( destination: ExSetInputView(exset: e), label: { Text(e.exercisename ?? "未命名") // 建议加个默认值,避免可选值为空崩溃 } ) }
完整代码示例:
import SwiftUI struct ExSetView: View { @Environment(\.managedObjectContext) var viewContext @Environment(\.dismiss) var dismiss @State var selectedItems = Set<Exercise>() var body: some View { NavigationView { VStack (alignment: .leading) { Text("Set Count: \(selectedItems.count)") ForEach(Array(selectedItems)) { e in NavigationLink( destination: ExSetInputView(exset: e), label: { Text(e.exercisename ?? "未命名") } ) } } } } }
方案二:直接用数组存储选中项
如果业务场景允许,把selectedItems的类型改成数组[Exercise],这样就不用每次转换了。但要注意数组允许重复元素,要是需要保证选中项唯一,得手动处理添加时的去重逻辑:
修改后的代码示例:
import SwiftUI struct ExSetView: View { @Environment(\.managedObjectContext) var viewContext @Environment(\.dismiss) var dismiss @State var selectedItems = [Exercise]() // 示例:添加选中项时去重 func addExercise(_ exercise: Exercise) { guard !selectedItems.contains(exercise) else { return } selectedItems.append(exercise) } var body: some View { NavigationView { VStack (alignment: .leading) { Text("Set Count: \(selectedItems.count)") ForEach(selectedItems) { e in NavigationLink( destination: ExSetInputView(exset: e), label: { Text(e.exercisename ?? "未命名") } ) } } } } }
额外注意点
如果Exercise是Core Data实体,要确保它已经遵循Identifiable协议(Core Data实体默认有objectID,只要在实体类中声明extension Exercise: Identifiable {}就行)。要是没遵循,ForEach需要手动指定id参数,比如:
ForEach(Array(selectedItems), id: \.objectID) { e in // ... }
内容的提问来源于stack exchange,提问作者Rachel
相关产品推荐
相关产品推荐

