【问题标题】:SwiftUI List View not updating after Core Data entity updated in another View在另一个视图中更新核心数据实体后,SwiftUI 列表视图未更新
【发布时间】:2020-06-30 04:44:14
【问题描述】:

我有一个存储在 Core Data 中的课程实体,其中一个变量是存储课程是否完整。

课程在 SwiftUI 列表中列出,选择后转到游戏所在的视图。游戏完成后,完整变量将更新为 true。应该发生的是列表视图显示列出的游戏,并在游戏旁边有一个复选标记。

但是,发生的情况是,当我在游戏中保存课程的“已完成”状态时(在 tapRight 方法中 - 见下文),我收到警告:

[TableView] 仅警告一次:UITableView 被告知布局其可见单元格和其他内容,而不是在视图层次结构中(表视图或其父视图之一尚未添加到窗口中)。

然后当我按下游戏视图顶部的导航按钮返回列表视图时,我发现游戏已经从列表中消失了。

但是,当我关闭应用程序并重新打开它时,列表中包含游戏行和复选标记,因此我知道核心数据课程实例正在正确更新。下面列表视图的代码。

import SwiftUI
import CoreData
import UIKit

struct LessonList: View {

@Environment(\.managedObjectContext) var moc

@State private var refreshing = false
private var didSave =  NotificationCenter.default.publisher(for: .NSManagedObjectContextDidSave)

@FetchRequest(entity: Lesson.entity(), sortDescriptors: [], predicate: NSPredicate(format: "(type == %@) AND (stage == %@) AND ((complete == %@) OR (complete == %@))", "phonicIntro", "1", NSNumber(value: true), NSNumber(value: false) )) var phonicIntroLessons1: FetchedResults<Lesson>

var body: some View {

    NavigationView {      
        List {
            self.stage1Section
        }
        .navigationBarTitle("Lessons")
    }
}


private var phonicIntroLink : some View {
    ForEach(phonicIntroLessons1) { lesson in
        NavigationLink(destination: PhonicIntroGame(lesson: lesson)) {
            LessonRow(lesson: lesson)
        }
    }
}

private var stage1Section : some View {
    Section(header: Text("Stage 1")) {
        phonicIntroLink
    }.onReceive(self.didSave) { _ in
        self.refreshing.toggle()
    }
}

游戏视图中保存完成状态的相关代码:

import SwiftUI
import AVFoundation

struct PhonicIntroGame: View {

@Environment(\.managedObjectContext) var moc

var lesson: Lesson?

func tapRight() {
    if ((self.lesson?.phonicsArray.count ?? 1) - 1) > self.index {
        self.index = self.index + 1
        print("This is index \(self.index)")
        //On completion
        if (index + 1)  == (lesson!.phonicsArray.count) {
            self.lessonComplete()
        }
    } else {
        print(self.index)

func lessonComplete() {
    self.lesson?.setComplete(true)
    self.saveLesson()
}

func saveLesson() {
    do {
        try moc.save()
    } catch {
        print("Error saving context \(error)")
    }
}

在 NSManagedObject 子类中:

extension Lesson {

func setComplete (_ state: Bool) {
    objectWillChange.send()
    self.complete = state
}
}

课程行代码如下:

import SwiftUI
import CoreData

struct LessonRow: View {

@Environment(\.managedObjectContext) var moc
@State var refreshing1 = false
var didSave =  NotificationCenter.default.publisher(for: .NSManagedObjectContextDidSave)
var lesson: Lesson

var body: some View {
        HStack {
            Image(lesson.wrappedLessonImage )
                .resizable()
                .frame(width: 80, height: 80)
                .cornerRadius(20)

            Text(lesson.wrappedTitle)
                .font(.system(size: 20))
                .fontWeight(.thin)
                .padding()

            if lesson.complete == true {
                Image(systemName: "checkmark.circle")
                    .resizable()
                    .frame(width: 30, height: 30)
                    .foregroundColor(.green)
            } else {
                Rectangle()
                    .frame(width: 30, height: 30)
                    .foregroundColor(.clear)
            }
        }.padding()
            .onReceive(self.didSave) { _ in
                self.refreshing1.toggle()
            }


   }
}

我尝试过的东西:

  1. 强制列表重新加载通知和 .onReceive
  2. 使用 setComplete 函数设置课程 NSManagedObject 子类,该函数调用“objectWillChange”
  3. 通过包含完整变量的 true 和 false 来使 @FetchRequest 更加详细

对于如何解决此问题的任何建议,我将不胜感激。我是 SwiftUI 的新手,所以提前感谢您的所有帮助。

【问题讨论】:

  • 您的代码无法编译。提供显示问题并且可以编译和运行的最少代码。这样更容易提供答案。
  • 感谢您的回复。我认为唯一相关的其他代码是我现在在上面添加的课程行。我试图只保留相关内容,以免信息过多。
  • 您的代码仍然无法编译。创建一个新项目并复制这篇文章中的代码并尝试编译。
  • 你能解决这个问题吗?
  • 是的,看看下面最后是如何解决的。必须作为答案,因为显然不能在 cmets 中放两个 @。

标签: swift core-data swiftui


【解决方案1】:

Core Data 批量更新不会更新内存中的对象。之后您必须手动刷新。

批处理操作绕过正常的核心数据操作,直接在底层 SQLite 数据库(或任何支持持久存储的东西)上操作。他们这样做是为了提高速度,但这意味着他们也不会触发您使用正常获取请求获得的所有内容。

您需要执行 Apple 的核心数据批处理编程指南中所示的操作:实施批处理更新 - 执行后更新您的应用程序

Original answer similar case similar case

【讨论】:

    【解决方案2】:

    是的,我最终解决这个问题的方法是将 @ObservedObject 添加到 PhonicIntroGame 中的存储对象:视图。如下-

    @ObservedObject var lesson: Lesson
    

    【讨论】:

    • 谢谢,我为此苦苦挣扎了好几个星期
    猜你喜欢
    • 2020-10-29
    • 2020-11-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多