SwiftUI List View not updating after Core Data entity updated in another View

不问归期 提交于 2020-03-25 12:31:11

问题


I have a Lesson entity stored in Core Data one of whose variables is to store whether the lesson is complete.

The lessons are listed in a SwiftUI list and when selected go to the View where the game exists. Once the game is completed, the complete variable is updated to true. And what is supposed to happen is that the list View displays the listed game with a check mark beside the game.

However, what is happening is that when I save the 'completed' state of the lesson in the game (in the tapRight method - see below), I get the warning:

[TableView] Warning once only: UITableView was told to layout its visible cells and other contents without being in the view hierarchy (the table view or one of its superviews has not been added to a window).

And then when I return to the list View by pushing the navigation button at the top of the game View, I find the game has disappeared from the list.

However, when I close the app and reopen it, the list is there with the row for the game and the checkmark, so I know the core data lesson instance is being updated correctly. Code for List view below.

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()
    }
}

Relevant code in game View for saving completed status:

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)")
    }
}

In the NSManagedObject Subclass:

extension Lesson {

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

The code for the lessonRow is as follows:

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()
            }


   }
}

The things I have tried:

  1. Forcing the list to reload with notifications and .onReceive
  2. Setting the Lesson NSManagedObject subclass with setComplete function, which calls 'objectWillChange'
  3. Making the @FetchRequest more detailed by including both true and false for the complete variable

I'd be grateful for any suggestions on how to resolve this. I'm new to SwiftUI so thanks in advance for all your help.

来源:https://stackoverflow.com/questions/60750558/swiftui-list-view-not-updating-after-core-data-entity-updated-in-another-view

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!