Bjö*_* B. 36 core-data swiftui combine
在 SwiftUI 中,View我有一个List基于@FetchRequest显示Primary实体和通过关系连接Secondary实体的数据。当我添加一个带有新的相关次要实体的新实体时,View和它List的更新正确Primary。
问题是,当我Secondary在详细视图中更新连接的项目时,数据库会更新,但更改未反映在Primary列表中。显然,@FetchRequest不会被另一个视图中的更改触发。
当我此后在主视图中添加新项目时,先前更改的项目最终会得到更新。
作为一种解决方法,我还更新Primary了详细信息视图中实体的属性,并将更改正确传播到Primary视图。
我的问题是:如何强制更新@FetchRequestsSwiftUI Core Data 中的所有相关内容?特别是,当我无法直接访问相关实体时/ @Fetchrequests?
import SwiftUI
extension Primary: Identifiable {}
// Primary View
struct PrimaryListView: View {
@Environment(\.managedObjectContext) var context
@FetchRequest(
entity: Primary.entity(),
sortDescriptors: [NSSortDescriptor(key: "primaryName", ascending: true)]
)
var fetchedResults: FetchedResults<Primary>
var body: some View {
List {
ForEach(fetchedResults) { primary in
NavigationLink(destination: SecondaryView(primary: primary)) {
VStack(alignment: .leading) {
Text("\(primary.primaryName ?? "nil")")
Text("\(primary.secondary?.secondaryName ?? "nil")").font(.footnote).foregroundColor(.secondary)
}
}
}
}
.navigationBarTitle("Primary List")
.navigationBarItems(trailing:
Button(action: {self.addNewPrimary()} ) {
Image(systemName: "plus")
}
)
}
private func addNewPrimary() {
let newPrimary = Primary(context: context)
newPrimary.primaryName = "Primary created at \(Date())"
let newSecondary = Secondary(context: context)
newSecondary.secondaryName = "Secondary built at \(Date())"
newPrimary.secondary = newSecondary
try? context.save()
}
}
struct PrimaryListView_Previews: PreviewProvider {
static var previews: some View {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
return NavigationView {
PrimaryListView().environment(\.managedObjectContext, context)
}
}
}
// Detail View
struct SecondaryView: View {
@Environment(\.presentationMode) var presentationMode
var primary: Primary
@State private var newSecondaryName = ""
var body: some View {
VStack {
TextField("Secondary name:", text: $newSecondaryName)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
.onAppear {self.newSecondaryName = self.primary.secondary?.secondaryName ?? "no name"}
Button(action: {self.saveChanges()}) {
Text("Save")
}
.padding()
}
}
private func saveChanges() {
primary.secondary?.secondaryName = newSecondaryName
// TODO: ? workaround to trigger update on primary @FetchRequest
primary.managedObjectContext.refresh(primary, mergeChanges: true)
// primary.primaryName = primary.primaryName
try? primary.managedObjectContext?.save()
presentationMode.wrappedValue.dismiss()
}
}
Run Code Online (Sandbox Code Playgroud)
G. *_*arc 64
我也为此苦苦挣扎,并找到了一个非常好的和干净的解决方案:
您必须将行包装在单独的视图中,并在实体的该行视图中使用 @ObservedObject。
这是我的代码:
酒单:
struct WineList: View {
@FetchRequest(entity: Wine.entity(), sortDescriptors: [
NSSortDescriptor(keyPath: \Wine.name, ascending: true)
]
) var wines: FetchedResults<Wine>
var body: some View {
List(wines, id: \.id) { wine in
NavigationLink(destination: WineDetail(wine: wine)) {
WineRow(wine: wine)
}
}
.navigationBarTitle("Wines")
}
}
Run Code Online (Sandbox Code Playgroud)
葡萄酒行:
struct WineRow: View {
@ObservedObject var wine: Wine // !! @ObserveObject is the key!!!
var body: some View {
HStack {
Text(wine.name ?? "")
Spacer()
}
}
}
Run Code Online (Sandbox Code Playgroud)
Asp*_*eri 27
您需要一个发布者,它会在主视图中生成有关上下文更改和某些状态变量的事件,以强制在接收来自该发布者的事件时重建视图。
重要提示:视图构建器代码中必须使用状态变量,否则渲染引擎将不知道发生了什么变化。
这是对代码受影响部分的简单修改,提供了您需要的行为。
@State private var refreshing = false
private var didSave = NotificationCenter.default.publisher(for: .NSManagedObjectContextDidSave)
var body: some View {
List {
ForEach(fetchedResults) { primary in
NavigationLink(destination: SecondaryView(primary: primary)) {
VStack(alignment: .leading) {
// below use of .refreshing is just as demo,
// it can be use for anything
Text("\(primary.primaryName ?? "nil")" + (self.refreshing ? "" : ""))
Text("\(primary.secondary?.secondaryName ?? "nil")").font(.footnote).foregroundColor(.secondary)
}
}
// here is the listener for published context event
.onReceive(self.didSave) { _ in
self.refreshing.toggle()
}
}
}
.navigationBarTitle("Primary List")
.navigationBarItems(trailing:
Button(action: {self.addNewPrimary()} ) {
Image(systemName: "plus")
}
)
}
Run Code Online (Sandbox Code Playgroud)
Joh*_*_Ye 13
另一种方法:使用 Publisher 和 List.id():
struct ContentView: View {
/*
@FetchRequest...
*/
private var didSave = NotificationCenter.default.publisher(for: .NSManagedObjectContextDidSave) //the publisher
@State private var refreshID = UUID()
var body: some View {
List {
...
}
.id(refreshID)
.onReceive(self.didSave) { _ in //the listener
self.refreshID = UUID()
print("generated a new UUID")
}
}
}
Run Code Online (Sandbox Code Playgroud)
每次在上下文中调用 NSManagedObjects 的 save() 时,它都会为列表视图生成一个新的 UUID,并强制刷新列表视图。
| 归档时间: |
|
| 查看次数: |
11126 次 |
| 最近记录: |