gpt4 book ai didi

ios - 如何将核心数据模型项传递到 View 中进行编辑

转载 作者:行者123 更新时间:2023-12-01 23:07:19 25 4
gpt4 key购买 nike

我在 CDPassingQ 有一个最小的示例项目

我的主要(ContentView)看起来像:

import SwiftUI
import CoreData

struct ContentView: View {
@Environment( \.managedObjectContext ) private var viewContext

@FetchRequest( sortDescriptors: [ NSSortDescriptor( keyPath: \Item.name, ascending: true ) ],
animation: .default )
private var items: FetchedResults<Item>

var body: some View {
NavigationView {
List {
ForEach( items ) { item in
NavigationLink {
NameViewer( itemID: item.objectID )
} label: {
Text( item.name! )
}
}
.onDelete( perform: deleteItems )
}
.toolbar {
ToolbarItem( placement: .navigationBarTrailing ) {
EditButton()
}

ToolbarItem {
Button() {
print( "Add Item" )
} label: {
NavigationLink {
NameViewer();
} label: {
Label( "Add Item", systemImage: "plus" )
}
}
}
}
}
}



private func deleteItems(offsets: IndexSet) {
withAnimation {
offsets.map { items[$0] }.forEach(viewContext.delete)

do {
try viewContext.save()
} catch {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
}
}
}


struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView().environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
}
}

NameViewer看起来像:

import SwiftUI
import CoreData

enum TrustReason: String, Identifiable, CaseIterable
{
var id: UUID
{
return UUID();
}

case unknown = "Unknown";
case legalOnly = "Legal Only";
case goodLabeling = "Good Labeling";
case facilityClean = "Facility Clean";
case detailedAnswers = "Detailed Answers";
case unresponsive = "Unresponsive";
}



extension TrustReason
{
var title: String
{
switch self
{
case .unknown:
return "Unknown";

case .legalOnly:
return "Legal Only";

case .goodLabeling:
return "Good Labeling";

case .facilityClean:
return "Facility Clean";

case .detailedAnswers:
return "Detailed Answers";

case .unresponsive:
return "Unresponsive";
}
}
}



struct NameViewer: View {
@Environment( \.presentationMode ) var presentationMode
@Environment( \.managedObjectContext ) private var moc

@State private var name: String = ""
@State private var reason: TrustReason = .unknown

var itemID: NSManagedObjectID?

var body: some View {
Form {
Section( header: Text( "Information" ) ) {
TextField( "Name", text: $name )
}

Section( header: Text( "Trust" ) ) {
Picker( "Reason", selection: $reason ) {
ForEach( TrustReason.allCases ) { trustReason in
Text( trustReason.title ).tag( trustReason )
}
}
}
}
.toolbar {
Button() {
if ( saveName() ) {
self.presentationMode.wrappedValue.dismiss()
}
} label: {
Text( "Save" )
}
}
.onAppear {
print( "on appear" )

guard let theID = itemID,
let item = moc.object( with: theID ) as? Item else {
return
}

print( "passed guard" )

if let itemName = item.name {
name = itemName
}

print( name )
}
}



private func saveName() -> Bool {
let item = Item( context: moc )

do {
print( self.name )

item.name = self.name

try moc.save()

return true
} catch {
print( error )
print( error.localizedDescription )
}

self.moc.rollback();

return false
}
}



struct NameViewer_Previews: PreviewProvider {
static var previews: some View {
NameViewer()
}
}

我可以创建新项目以显示在 ContentView 的列表中。

然后,当我在列表中选择一个项目时,我将该项目传递给 NameViewer。我可以确认我在 .onAppear 代码中成功找到了正确的对象。

但是,有两个问题:

  1. 如果我在列表中选择一个项目,除非我先点击文本字段,否则项目名称不会出现在名称文本字段中。'

  2. 使用 .onAppear 似乎不是放置该代码的正确位置。原因是 Picker 将另一个 View 插入堆栈,一旦项目被选中,.onAppear 再次运行并且我丢失了对名称字段的更改名称。

如何更改代码以解决这些问题?

最佳答案

为了实现所需的功能,我会在 UI 和核心数据方面更改您的架构。

在用户界面方面,最好使用导航链接来显示静态数据详细 View ,并使用模式来执行数据操作,例如创建和编辑对象。因此,有一个 View 显示对象详细信息(例如 NameViewer),另一个 View 用于编辑对象(例如 NameEditor)。此外,将 NSManagedObject 子类的属性直接绑定(bind)到 SwiftUI 控件。不要创建额外的 @State 属性,然后复制值。您正在引入一个共享状态,这是 SwiftUI 旨在消除的东西。

在核心数据方面,为了执行创建和更新操作,您需要使用子上下文。每当您创建或更新对象时,都会显示一个注入(inject)了子上下文的模态编辑器 View 。这样,如果我们对我们的更改不满意,我们可以简单地忽略该模式,并且更改会被神奇地丢弃,而无需调用 rollback(),因为该子上下文已随 View 一起销毁。由于您现在正在使用子上下文,因此不要忘记将主视图上下文也保存在某个地方,例如当用户导航出您的应用时。

所以为了在代码中实现它,我们需要一些结构来存储我们新创建的对象以及它们的子上下文:

struct CreateOperation<Object: NSManagedObject>: Identifiable {
let id = UUID()
let childContext: NSManagedObjectContext
let childObject: Object

init(with parentContext: NSManagedObjectContext) {
let childContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
childContext.parent = parentContext
let childObject = Object(context: childContext)

self.childContext = childContext
self.childObject = childObject
}
}

struct UpdateOperation<Object: NSManagedObject>: Identifiable {
let id = UUID()
let childContext: NSManagedObjectContext
let childObject: Object

init?(
withExistingObject object: Object,
in parentContext: NSManagedObjectContext
) {
let childContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
childContext.parent = parentContext
guard let childObject = try? childContext.existingObject(with: object.objectID) as? Object else { return nil }

self.childContext = childContext
self.childObject = childObject
}
}

UI代码如下:

struct ContentView: View {
@Environment(\.managedObjectContext) private var viewContext
@FetchRequest(
sortDescriptors: [NSSortDescriptor(keyPath: \Item.name, ascending: true)], animation: .default
) private var items: FetchedResults<Item>
@State private var itemCreateOperation: CreateOperation<Item>?

var body: some View {
NavigationView {
List {
ForEach(items) { item in
NavigationLink {
NameViewer(item: item)
} label: {
Text(item.name ?? "")
}
}
}
.toolbar {
ToolbarItemGroup(placement: .navigationBarTrailing) {
EditButton()
Button(action: {
itemCreateOperation = CreateOperation(with: viewContext)
}) {
Label("Add Item", systemImage: "plus")
}
}
}
.sheet(item: $itemCreateOperation) { createOperation in
NavigationView {
NameEditor(item: createOperation.childObject)
.navigationTitle("New Item")
}
.environment(\.managedObjectContext, createOperation.childContext)
}
}
}
}

struct NameViewer: View {
@Environment(\.managedObjectContext) private var viewContext
@State private var itemUpdateOperation: UpdateOperation<Item>?

@ObservedObject var item: Item

var body: some View {
Form {
Section {
Text(item.name ?? "")
}
}
.navigationTitle("Item")
.toolbar {
Button("Update") {
itemUpdateOperation = UpdateOperation(withExistingObject: item, in: viewContext)
}
}
.sheet(item: $itemUpdateOperation) { updateOperation in
NavigationView {
NameEditor(item: updateOperation.childObject)
.navigationTitle("Update Item")
}
.environment(\.managedObjectContext, updateOperation.childContext)
}
}
}

struct NameEditor: View {
@Environment(\.dismiss) private var dismiss
@Environment(\.managedObjectContext) private var childContext

@ObservedObject var item: Item

var body: some View {
Form {
Section(header: Text("Information")) {
if let name = Binding($item.name) {
TextField("Name", text: name)
}
}
}
.toolbar {
Button() {
try? childContext.save()
dismiss()
} label: {
Text("Save")
}
}
}
}

更多信息见我的相关回答:

关于ios - 如何将核心数据模型项传递到 View 中进行编辑,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70643389/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com