SwiftUI中如何传递含数组的可选类实现CoreData明细属性绑定
问题原因
你的代码无法编译、无法实现修改效果,核心是三个写法错误:
- 使用
@State存储NSManagedObject引用类型实例,无法监听托管对象的属性变化 - 对只读计算属性
listitemsArray取投影绑定,该属性每次访问都会生成新的排序数组,不存在可写入的合法绑定路径 - 对引用类型的CoreData对象错误使用
@Binding传值,Binding的值语义设计不适用于NSManagedObject这类引用类型
解决方案
该需求完全可以实现,且不需要修改你现有任何属性的可选类型定义,修正逻辑如下:
NSManagedObject默认遵守ObservableObject协议,持有托管对象时使用@ObservedObject包装才能监听变化- 遍历一对多关系的排序结果时,直接遍历数组元素即可,不需要对数组本身做绑定:
Item是引用类型,传递实例本身就是操作同一个托管对象,修改属性会直接作用到原数据源 - 子视图使用
@ObservedObject持有Item实例,即可直接生成属性的双向绑定给UI控件使用
修正后的完整代码:
import SwiftUI import Foundation import CoreData class Item: NSManagedObject, Identifiable { @NSManaged public var sequence: Int @NSManaged public var txt: String } class Stuff: NSManagedObject { @NSManaged public var name: String? @NSManaged public var listitems: NSSet? public var listitemsArray: [Item] { let set = listitems as? Set<Item> ?? [] return set.sorted { $0.sequence < $1.sequence } } } struct ContentView: View { @State var stuff: Stuff? var body: some View { VStack { if let stuff = stuff { // 拆出非可选视图处理托管对象监听 StuffDetailView(stuff: stuff) } } } } // 内部非可选视图,用ObservedObject监听Stuff变化 struct StuffDetailView: View { @ObservedObject var stuff: Stuff var body: some View { // 直接遍历排序后的数组,不需要对数组取绑定 ForEach(stuff.listitemsArray) { item in ItemView(item: item) } } } struct ItemView: View { // 引用类型用ObservedObject持有,不需要@Binding @ObservedObject var item: Item var body: some View { HStack { // 直接通过ObservedObject的投影获取属性绑定 TextField("Item...", text: $item.txt) } } }
补充说明
如果后续你需要绑定Item或Stuff中的可选字符串属性(比如Stuff.name),不需要修改属性本身的可选定义,只要自定义空值兜底的Binding即可,示例:
// 绑定可选String属性的示例写法 TextField("Name...", text: Binding( get: { stuff.name ?? "" }, set: { stuff.name = $0.isEmpty ? nil : $0 } ))
所有修改会直接作用到CoreData托管对象,你只需要在业务需要的时机调用NSManagedObjectContext.save()即可完成持久化。
内容的提问来源于stack exchange,提问作者Russ
相关产品推荐
相关产品推荐

