在父视图中,我有这个:
LongPressEditableText(contents: "(workout.name ?? "")", context: workout, keyPath: WorkoutEntity.name)
在CoreData中引用WorkoutEntity的字符串字段。
LongPressEditableText是一个组件,它通常只是一个Text(),但是当长按时,变成一个具有相同内容的TextField,可编辑。在提交时,它应该更新UI(它做得很好),但它也应该将新值保存到CoreData中的适当位置。
struct LongPressEditableText: View {
@State var contents: String
@Environment(.managedObjectContext) private var viewContext
var context: NSObject
var keyPath: KeyPath<NSObject, String?>
@State var inEditMode: Bool = false
var body: some View {
if inEditMode {
TextField("test", text: $contents)
.onSubmit {
context[keyPath: keyPath] = contents
do {
try viewContext.save()
} catch {
let nsError = error as NSError
fatalError("Unresolved error (nsError), (nsError.userInfo)")
}
inEditMode.toggle()
}
} else {
Text(contents)
.onLongPressGesture {
inEditMode.toggle()
}
}
}
}
现在,我得到了两个错误。在我的父视图Cannot convert value of type 'KeyPath<WorkoutEntity, String?>' to expected argument type 'KeyPath<NSObject, String?>'
和LongPressEditableText视图Cannot assign through subscript: key path is read-only
我可以通过强制KeyPath来解决第一个问题,但这不是一个解决方案,因为我希望可编辑字段与许多具有字符串字段的不同实体一起工作,所以我希望它是通用的。第二次我被难住了,这是我最接近成功的一次。
"泛型不是我主要关心的…">,是的,这是因为它是一个非常有用的解决方案,告诉编译器和运行时在文本字段中使用什么类型的对象。
首先,因为这是核心数据,我们不应该使用NSObject,而是NSManagedObject,所以让我们用一个继承自NSManagedObject
的类型来创建视图泛型,然后在属性中使用泛型。
struct LongPressEditableText<ManagedObject: NSManagedObject>: View {
@Environment(.managedObjectContext) private var viewContext
@State private var contents: String = ""
@State var object: ManagedObject
var keyPath: ReferenceWritableKeyPath <ManagedObject, String?>
注意,属性object
(代码中的上下文)被声明为泛型类型,并且keyPath
也被定义为持有相同的类型。我也从KeyPath
更改为ReferenceWritableKeyPath
,因为泛型类型是一个类,我们想使用密钥路径来更新对象。
在这里使用字段是一个例子,因为视图是泛型的,编译器可以推断出泛型类型是Item
,并检查它是否有一个属性text
struct DetailView: View {
@ObservedObject var item: Item
var body: some View {
VStack {
LongPressEditableText(object: item, keyPath: .text)
}
.padding()
}
}