如何强制刷新SwiftUI的FetchedResults以更新视图?
解决SwiftUI中FetchedResults无法随关联实体更新的问题
问题根源
- 直接在
Body中修改tokens.nsPredicate违反SwiftUI状态管理规则——Body仅用于描述视图,不应执行状态变更操作。 - Core Data的关联集合(如
scenario.scenarioTokenArray)默认不会主动发布变更通知,即便scenario是@ObservedObject,关联实体的新增也无法触发视图刷新,导致FetchedResults不会重新计算谓词。
方案一:结合objectWillChange动态更新谓词(推荐)
利用Core Data实体自带的objectWillChange发布者,监听scenario的关联变化,手动更新FetchRequest的谓词,触发FetchedResults重新拉取数据。
struct TokenListView: View { @ObservedObject var scenario: Scenario @Environment(\.managedObjectContext) private var moc // 初始化时传入初始谓词 @FetchRequest private var tokens: FetchedResults<Token> init(scenario: Scenario) { self.scenario = scenario _tokens = FetchRequest( sortDescriptors: [SortDescriptor(\.label)], predicate: NSPredicate(format: "NOT (id IN %@)", scenario.scenarioTokenArray.compactMap { $0.token?.id?.uuidString }), animation: .default ) } var body: some View { List(tokens) { token in Button(token.label ?? "") { addTokenToScenario(token: token) } } // 监听scenario的变更通知,更新谓词 .onReceive(scenario.objectWillChange) { _ in tokens.nsPredicate = NSPredicate(format: "NOT (id IN %@)", scenario.scenarioTokenArray.compactMap { $0.token?.id?.uuidString }) } } private func addTokenToScenario(token: Token) { let scenarioToken = ScenarioToken(context: moc) scenarioToken.token = token scenario.addToScenarioToken(scenarioToken) // 必须保存上下文,确保变更被持久化并通知所有观察者 try? moc.save() } }
方案二:用@State包装排除ID列表
将需要排除的Token ID存为@State变量,通过监听关联集合变化或手动更新该变量,触发FetchRequest的谓词更新。
struct TokenListView: View { @ObservedObject var scenario: Scenario @Environment(\.managedObjectContext) private var moc @State private var excludedTokenIds: [String] = [] @FetchRequest(sortDescriptors: [SortDescriptor(\.label)]) private var tokens: FetchedResults<Token> var body: some View { List(tokens) { token in Button(token.label ?? "") { addTokenToScenario(token: token) } } .onAppear { updateExcludedIds() } // 监听关联集合数量变化,更新排除ID .onChange(of: scenario.scenarioTokenArray.count) { _ in updateExcludedIds() } // 排除ID变化时,更新谓词 .onChange(of: excludedTokenIds) { newIds in tokens.nsPredicate = NSPredicate(format: "NOT (id IN %@)", newIds) } } private func updateExcludedIds() { excludedTokenIds = scenario.scenarioTokenArray.compactMap { $0.token?.id?.uuidString } } private func addTokenToScenario(token: Token) { let scenarioToken = ScenarioToken(context: moc) scenarioToken.token = token scenario.addToScenarioToken(scenarioToken) try? moc.save() // 手动更新排除ID,强制触发视图刷新 updateExcludedIds() } }
关键注意事项
- 绝对不要在
Body中直接修改FetchRequest的谓词,所有状态变更应通过@State、@ObservedObject的发布者或onReceive/onChange触发。 - 必须调用
moc.save(),否则Core Data的变更仅存在于内存中,不会触发全局的变更通知。 - 若关联集合频繁变化,优先使用方案一,它能直接监听实体的所有变更,无需手动维护状态变量。
内容的提问来源于stack exchange,提问作者lcj
相关产品推荐
相关产品推荐

