过滤的关系计数未在父视图中更新 [英] Filtered relationship count not updating in parent view
问题描述
在我的父视图中,我有以下代码:
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屋!