过滤的关系计数未在父视图中更新 [英] Filtered relationship count not updating in parent view

查看:24
本文介绍了过滤的关系计数未在父视图中更新的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的父视图中,我有以下代码:

Text(String((todoList.todoItems as!Set).filter({ $0.checked }).count))文本("/")文本(字符串(todoList.todoItems?.计数??0))

例如,如果我让 checked 关闭了 5 个任务中的 2 个,我会得到 2/5.

todoItems 更改时,第一个计数(带过滤器)不会更新,但第二个计数会更新.如果我完全关闭应用程序并重新打开它,第一个计数确实显示正确的数字.

如何在关系发生变化时让第一个数字像第二个一样更新,而无需重新启动应用程序?

解决方案

一切都与您使用 CoreData 有关.NSObject 类有一个错误.您可以通过向您的子类添加扩展来修复它,如

这里是数据模型

和完整代码:

导入 SwiftUI导入核心数据@objc(ToDoItem)公共类 ToDoItem: NSManagedObject , Identifiable{公共变量 ID = UUID()}扩展待办事项{@nonobjc 公共类 func fetchRequest() ->NSFetchRequest{return NSFetchRequest(entityName: "ToDoItem")}@NSManaged 公共变量标题:字符串?@NSManaged public var toCheck:NSSet?公共变量检查列表:[CheckItem]{让 set = toCheck as?设置??[]返回 set.sorted(){$0.wrappedName >$1.wrappedName}}公共变量检查项目:[检查项目]{让 set = toCheck as?设置??[]返回 set.filter({$0.checked}).sorted(){$0.wrappedName >$1.wrappedName}}覆盖 public func willChangeValue(forKey key: String) {super.willChangeValue(forKey: key)self.objectWillChange.send()}}//MARK: 为 toCheck 生成的访问器扩展待办事项{@objc(addToCheckObject:)@NSManaged public func addToToCheck(_ value: CheckItem)@objc(removeToCheckObject:)@NSManaged public func removeFromToCheck(_ value: CheckItem)@objc(addToCheck:)@NSManaged public func addToToCheck(_ values: NSSet)@objc(removeToCheck:)@NSManaged public func removeFromToCheck(_ values: NSSet)静态函数 allItemsFetchRequest() ->NSFetchRequest{让 fetchRequest: NSFetchRequest= ToDoItem.fetchRequest()fetchRequest.sortDescriptors = []返回 fetchRequest}}@objc(CheckItem)公共类 CheckItem: NSManagedObject, Identifiable {公共变量 ID = UUID()}扩展检查项{覆盖 public func willChangeValue(forKey key: String) {super.willChangeValue(forKey: key)self.objectWillChange.send()}@nonobjc 公共类 func fetchRequest() ->NSFetchRequest{return NSFetchRequest(entityName: "CheckItem")}@NSManaged 公共变量名:字符串?公共变量包装名称:字符串{姓名 ??无名"}@NSManaged 公共变量检查:布尔公共变量检查触发器:布尔{得到{检查}放{如果让 todo = self.toDo {todo.willChangeValue(forKey: "checkedItems")}选中 = 新值}}@NSManaged 公共变量 toDo:ToDoItem?}结构内容视图:查看{@Environment(\.managedObjectContext) var moc@FetchRequest(fetchRequest: ToDoItem.allItemsFetchRequest()) var toDoItems: FetchedResultsvar主体:一些视图{虚拟堆栈{列表{ForEach(self.toDoItems){toDoItemToDoView(toDoItem: toDoItem)}}堆栈{Button("添加测试项"){让 toDo = ToDoItem(上下文:self.moc)toDo.title = "一些标题"让 check1 = CheckItem(上下文:self.moc)check1.name = "来检查项目"check1.toDo = 待办事项让 check2 = CheckItem(上下文:self.moc)check2.name = "另一个要检查的项目"check2.toDo = 待办事项}垫片()按钮(保存"){如果 self.moc.hasChanges{做{尝试 self.moc.save()}抓住{打印(无法保存更改:error.localizedDescription")}}}}}}}struct ToDoView:查看{@ObservedObject var toDoItem: ToDoItemvar body: 一些视图{虚拟堆栈{文本(toDoItem.title!)文本(检查:\(toDoItem.checkedItems.count)/\(toDoItem.checkList.count)")ForEach(toDoItem.checkList){签入检查视图(检查项目:检查)}}}}结构检查视图:查看{@ObservedObject var checkItem: CheckItemvar body: 一些视图{堆栈{文本(checkItem.name ??未命名")Toggle("checked:", isOn: $checkItem.checkedTrigger)}}}struct ContentView_Previews: PreviewProvider {静态 var 预览:一些视图 {让上下文 = (UIApplication.shared.delegate as!AppDelegate).persistentContainer.viewContext返回 ContentView().environment(\.managedObjectContext, context)}}

In my parent view I have the following code:

Text(String((todoList.todoItems as! Set<TodoItem>).filter({ $0.checked }).count))
Text("/")
Text(String(todoList.todoItems?.count ?? 0))

For example, if I had checked off 2 out of 5 tasks, I would get 2/5.

The first count (with the filter) is not updating when todoItems change, but the 2nd one is. If I completely close the app and re-open it the first count does show the correct number.

How do I get the first number to update like the second when the relationship changes, without having to relaunch the app?

解决方案

Its all about you using CoreData. NSObject class has a bug. You can fix it by adding extension to your subclass, as it was said here by @Anthony

override public func willChangeValue(forKey key: String) {
    super.willChangeValue(forKey: key)
    self.objectWillChange.send()
}

By the way, you can use a computed property instead of NSSet collection like this:

public var checkedItems: [CheckItem]{
     let set = toCheck as? Set<CheckItem> ?? []
    return set.filter({$0.checked}).sorted(){$0.wrappedName > $1.wrappedName}
}

after that you can use it simply in ForeEach.

Another computed property helps to push notification to the parent object:

public var checkedTrigger: Bool{
    get{
        checked
    }
    set{
        if let todo = self.toDo {
            todo.willChangeValue(forKey: "checkedItems")
        }
        checked = newValue
    }
}

Then just put this new property in a Toggle("checked:", isOn: $checkItem.checkedTrigger)

Here is DataModel

and full code:

import SwiftUI
import CoreData

@objc(ToDoItem)
public class ToDoItem: NSManagedObject , Identifiable{
    public var id = UUID()
}

extension ToDoItem {

    @nonobjc public class func fetchRequest() -> NSFetchRequest<ToDoItem> {
        return NSFetchRequest<ToDoItem>(entityName: "ToDoItem")
    }

    @NSManaged public var title: String?
    @NSManaged public var toCheck: NSSet?
    public var checkList: [CheckItem]{
         let set = toCheck as? Set<CheckItem> ?? []
         return set.sorted(){$0.wrappedName > $1.wrappedName}
    }
    public var checkedItems: [CheckItem]{
         let set = toCheck as? Set<CheckItem> ?? []
        return set.filter({$0.checked}).sorted(){$0.wrappedName > $1.wrappedName}
    }

    override public func willChangeValue(forKey key: String) {
        super.willChangeValue(forKey: key)
        self.objectWillChange.send()
    }
}

// MARK: Generated accessors for toCheck
extension ToDoItem {

    @objc(addToCheckObject:)
    @NSManaged public func addToToCheck(_ value: CheckItem)

    @objc(removeToCheckObject:)
    @NSManaged public func removeFromToCheck(_ value: CheckItem)

    @objc(addToCheck:)
    @NSManaged public func addToToCheck(_ values: NSSet)

    @objc(removeToCheck:)
    @NSManaged public func removeFromToCheck(_ values: NSSet)

    static func allItemsFetchRequest() -> NSFetchRequest<ToDoItem>{
        let fetchRequest: NSFetchRequest<ToDoItem> = ToDoItem.fetchRequest()
        fetchRequest.sortDescriptors = []
        return fetchRequest
    }

}

@objc(CheckItem)
public class CheckItem: NSManagedObject, Identifiable {
    public var id = UUID()
}

extension CheckItem {
    override public func willChangeValue(forKey key: String) {
        super.willChangeValue(forKey: key)
        self.objectWillChange.send()
    }
    @nonobjc public class func fetchRequest() -> NSFetchRequest<CheckItem> {
        return NSFetchRequest<CheckItem>(entityName: "CheckItem")
    }

    @NSManaged public var name: String?
    public var wrappedName: String{
        name ?? "unnamed"
    }
    @NSManaged public var checked: Bool
    public var checkedTrigger: Bool{
        get{
            checked
        }
        set{
            if let todo = self.toDo {
                todo.willChangeValue(forKey: "checkedItems")
            }
            checked = newValue
        }
    }
    @NSManaged public var toDo: ToDoItem?
}

struct ContentView: View {

    @Environment(\.managedObjectContext) var moc
    @FetchRequest(fetchRequest: ToDoItem.allItemsFetchRequest()) var toDoItems: FetchedResults<ToDoItem>

    var body: some View {
        VStack{
            List{
                ForEach(self.toDoItems){toDoItem in
                    ToDoView(toDoItem: toDoItem)
                }
            }
            HStack{
                 Button("add test items"){
                    let toDo = ToDoItem(context: self.moc)
                    toDo.title = "some title"
                    let check1 = CheckItem(context: self.moc)
                    check1.name = "come item to Check"
                    check1.toDo = toDo
                    let check2 = CheckItem(context: self.moc)
                    check2.name = "another item to Check"
                    check2.toDo = toDo
                }
                Spacer()
                Button("save"){
                    if self.moc.hasChanges{
                        do{
                            try self.moc.save()
                        }catch{
                            print("cant save changes: error.localizedDescription")
                        }
                    }
                }
            }
        }
    }
}

struct ToDoView: View {
    @ObservedObject var toDoItem: ToDoItem
    var body: some View{
        VStack{
            Text(toDoItem.title!)
            Text("checked: \(toDoItem.checkedItems.count)/\(toDoItem.checkList.count)")
            ForEach(toDoItem.checkList){check in
                CheckView(checkItem: check)
            }

         }
    }
}

struct CheckView: View{
    @ObservedObject var checkItem: CheckItem
    var body: some View{
        HStack{
            Text(checkItem.name ?? "unnamed" )
            Toggle("checked:", isOn: $checkItem.checkedTrigger)
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
        return ContentView().environment(\.managedObjectContext, context)
    }
}

这篇关于过滤的关系计数未在父视图中更新的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆