SwiftUI中如何设置依赖FetchRequest结果的@State数组?
问题解决:SwiftUI + CoreData 中 DisclosureGroup 展开状态数组索引越界崩溃
问题原因
你当前用@State var isListExpanded: [Bool]存储展开状态,数组长度仅在ContentView初始化时根据当时的学生数量设置。当点击"Add"按钮新增学生后,students的数量增加,但isListExpanded数组长度未同步更新,导致ForEach遍历students.indices时,访问的索引超出数组范围,触发Index out of range错误。
此外,直接依赖数组索引绑定状态还有隐藏问题:CoreData的FetchedResults可能因排序、数据变更等改变元素顺序,此时索引对应的学生对象会错乱,导致展开状态和学生不匹配。
解决方案
改用字典存储每个学生的展开状态,利用学生对象的id(UUID)作为字典的key,这样新增学生时只需为新的id添加默认状态,无需维护数组长度,同时避免索引顺序变化带来的状态错乱问题。
修改后的完整代码
1. StudentsApp.swift(简化初始化逻辑)
import SwiftUI import CoreData @main struct StudentsApp: App { @StateObject private var dataController = DataController() var body: some Scene { WindowGroup { ContentView() .environment(\.managedObjectContext, dataController.container.viewContext) } } }
2. DataController.swift(无需修改)
import Foundation import CoreData class DataController: ObservableObject { let container = NSPersistentContainer(name: "Students") init() { container.loadPersistentStores { description, error in if let error = error { print("Core Data failed to load: \(error.localizedDescription)") } } } }
3. ContentView.swift(核心修改)
import SwiftUI struct ContentView: View { @Environment(\.managedObjectContext) var moc @FetchRequest(sortDescriptors: []) var students: FetchedResults<Student> @State private var expandedStates: [UUID: Bool] = [:] // 用字典存储每个学生的展开状态 var body: some View { VStack { List { // 直接遍历学生对象,而非索引,避免顺序变化导致的状态错乱 ForEach(students) { student in Section { // 利用字典的默认值,确保新学生默认处于折叠状态 DisclosureGroup(isExpanded: $expandedStates[student.id!, default: false]) { Text("Expanded") } label: { Text(student.name ?? "Unknown") } } } } Button("Add") { let firstNames = ["Ginny", "Harry", "Hermione", "Luna", "Ron"] let lastNames = ["Granger", "Lovegood", "Potter", "Weasley"] let chosenFirstName = firstNames.randomElement()! let chosenLastName = lastNames.randomElement()! let student = Student(context: moc) student.id = UUID() student.name = "\(chosenFirstName) \(chosenLastName)" try? moc.save() // 可选:显式设置新学生的展开状态为false,省略也可,因为字典访问有default值 expandedStates[student.id!] = false } } } }
额外说明
- 移除了
StudentsApp中不必要的手动fetch操作,因为ContentView的@FetchRequest会自动监听CoreData上下文变化,实时更新学生列表。 - 直接遍历
students对象而非索引,符合SwiftUI的最佳实践,避免因数据顺序变化导致的UI与状态不匹配问题。 - 字典的
default: false确保新增学生时,即使未显式添加key,DisclosureGroup也能正常获取默认的折叠状态。
内容的提问来源于stack exchange,提问作者Dogahe
相关产品推荐
相关产品推荐

