SwiftUI如何在同结构体中结合@FetchRequest与@ObservedObject实时更新UI
解决方案
1. 无需拆分父子视图的实现方式
你不需要单独拆分出子视图,只要在ForEach的遍历闭包中,把遍历到的Core Data实体对象声明为@ObservedObject即可,SwiftUI 5.3及以上版本都支持闭包参数加属性包装器,修改后的代码如下:
struct MainView: View { @Environment(\.managedObjectContext) private var moc @FetchRequest(entity: CDUser.entity(), sortDescriptors: []) var users: FetchedResults<CDUser> var body: some View { List { // 直接在闭包参数前加@ObservedObject监听单个实体变更 ForEach(users, id: \.self) { @ObservedObject item in Button(action: { item.count = item.count + 1 do { try moc.save() } catch { print(error) } }, label: { Text(String(item.count)) }) } } } }
这样修改后,点击按钮修改实体属性时,@ObservedObject会直接触发当前视图刷新,不需要等页面重新出现。
2. 分组不自动更新的问题修复
你补充的分组场景不更新,核心原因是@FetchRequest的排序规则没有包含logged字段,当logged属性变更时,FetchRequest无法感知到该变更需要触发列表分组的重新计算,只需要给FetchRequest添加对应的排序器即可:
struct MainView: View { @Environment(\.managedObjectContext) private var moc // 给FetchRequest添加按logged字段排序的规则,logged变更时会自动重排结果 @FetchRequest(entity: CDUser.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \CDUser.logged, ascending: false)]) var users: FetchedResults<CDUser> var body: some View { List { Section(header: Text("Logged in")) { ForEach(users.filter({ $0.logged == true }), id: \.self) { @ObservedObject user in Button(action: { user.logged = false do { try moc.save() } catch { print(error) } }, label: { Text(user.firstName ?? "") }) } } Section(header: Text("Logged out")) { ForEach(users.filter({ $0.logged == false }), id: \.self) { @ObservedObject user in Button(action: { user.logged = true do { try moc.save() } catch { print(error) } }, label: { Text(user.firstName ?? "") }) } } } } }
如果还需要额外的排序规则,比如按用户名排序,可以继续在sortDescriptors数组里追加对应的NSSortDescriptor即可。
内容的提问来源于stack exchange,提问作者Peanutsmasher
相关产品推荐
相关产品推荐

