【问题标题】:Edit details of a dynamic list of core data object编辑核心数据对象动态列表的详细信息
【发布时间】:2021-02-28 14:41:35
【问题描述】:

我使用 Xcode 默认的 CoreData 模板来构建我的应用程序。 我曾尝试使用 CoreData 并创建这样的实体:

然后我创建了一个 AddItemView,它允许我将项目添加到视图中。

struct AddItemView: View {
@Environment(\.managedObjectContext) var viewContext
@Environment(\.presentationMode) var presentationMode

@State private var notes = ""
@State private var selectedDate = Date()
    
var body: some View {
    NavigationView {
        Form {
            Section {
                TextField("notes", text: $notes)
            }
            
            Section {
                DatePicker("", selection: $selectedDate, displayedComponents: .date)
                Text("Your selected date: \(selectedDate)")
            }
            
            Section {
                Button("Save") {
                    let newItem = Item(context: self.viewContext)
                    newItem.notes = self.notes
                    newItem.recordDate = self.selectedDate
                    newItem.timestamp = Date()
                    
                    try? self.viewContext.save()
                    self.presentationMode.wrappedValue.dismiss()
                }
                
            }
        }
        .navigationBarTitle("Add Item")
    }
}

}

它运行良好,可以添加项目。

然后我想单击每个项目以转到详细信息视图。在 DetailView 中,应该有一个编辑按钮,让我可以修改对象。

因此我为此目的创建了三个文件:ItemHost、DetailView、EditorView

项目的导航目的地将转到 ItemHost。

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

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

@State private var showingAddScreen = false

var body: some View {
    NavigationView {
        List {
            ForEach(items, id: \.self) { item in
                NavigationLink(destination: ItemHost(item: item)) {
                    VStack {
                        Text("Item at \(item.timestamp!, formatter: FormatterUtility.dateTimeFormatter)")
                        Text("notes: \(item.notes ?? "")")
                        Text("Item Date: \(item.recordDate!, formatter: FormatterUtility.dateFormatter)")
                    }
                }
            }
            .onDelete(perform: deleteItems)
        }
        .toolbar {
            
            ToolbarItem(placement: .navigationBarLeading) {
                #if os(iOS)
                EditButton()
                #endif
            }

            ToolbarItem(placement: .navigationBarTrailing) {
                Button(action: {self.showingAddScreen.toggle()}) {
                    Label("Add Item", systemImage: "plus")
                }
            }
        }
        .sheet(isPresented: $showingAddScreen) {
            AddItemView().environment(\.managedObjectContext, self.viewContext)
        }
    }
}

ItemHost如下:

struct ItemHost: View {

@Environment(\.editMode) var editMode
@Environment(\.managedObjectContext) var contextView

@State var item: Item

var body: some View {

    NavigationView {
        if editMode?.wrappedValue == .active {
            Button("Cancel") {
                editMode?.animation().wrappedValue = .inactive
            }           
        }
        
        if editMode?.wrappedValue == .inactive {
            ItemDetailView(item: item)
        } else {
            ItemEditor(item: item)
        }
    }.navigationBarTitle("EditMode Problem")
    .navigationBarItems(trailing: EditButton())     
}

}

DetailView只是一个显示细节的视图,没有什么特别的。

struct ItemDetailView: View {

@Environment(\.managedObjectContext) var contextView
@Environment(\.presentationMode) var presentationMode
@State private var showingDeleteAlert = false

let item: Item

var body: some View {
    VStack {
        Text("notes: \(item.notes ?? "")")
        Text("Record Date: \(item.recordDate!, formatter: FormatterUtility.dateFormatter)")
    }
    .navigationBarTitle(Text("Item Detail"), displayMode: .inline)
    .alert(isPresented: $showingDeleteAlert) {
        Alert(title: Text("Delete Item"), message: Text("Are you sure?"),
              primaryButton: .destructive(Text("Delete")) {
                self.deleteItem()
            }, secondaryButton: .cancel()
        )
    }
    .navigationBarItems(trailing: Button(action: {
        self.showingDeleteAlert = true
    }) {
        Image(systemName: "trash")
    })
    
}

// Problem here
// Can delete the item and go back to list page. But the actual item in the CoreData has not been removed. If I call contextView.save() it will crash.
func deleteItem() {
    contextView.delete(item)
    presentationMode.wrappedValue.dismiss()
}

}

EditorView 是这样的:

struct ItemEditor: View {

@Environment(\.presentationMode) var presentation
@State var item: Item
    
var body: some View {
    
    List {
        HStack {
            Text("Notes").bold()
            TextField("Notes", text: $item.notes) // Error
        }

        // Error
        DatePicker(selection: $item.recordDate, displayedComponents: .date) {
            Text("Record Date").bold()
        }
    }
    
}

}

这里有几个问题:

  1. ItemEditor:无法将“Binding”类型的值转换为预期的参数类型“Binding”。我无法选择原始项目对象值并显示它以让用户知道对象内的旧值是什么。

  2. 单击单个导航项后,将不会显示任何内容。我希望它最初(不是编辑模式)然后显示详细视图。如果是编辑模式,则显示编辑器。

我对@binding 以及如何将项目传递到 DetailView 和编辑器感到困惑。编辑器如何将数据保存回 contextView 中的 item 对象?

  1. 对于 ItemDetailView 中的 deleteItem()。它可以删除该项目并显然返回到 ItemListView。但是,当我退出应用程序,然后再次运行时。我发现该项目再次出现,并没有真正删除。

现在单击该项目,它会显示:

【问题讨论】:

  • 那是很多代码,我注意到的东西很少,你已经用“状态”属性包装器标记了你的“项目”实体,核心数据实体是 ManagedObject 子类,而不是结构,所以你应该' t 用“状态”标记它们,而不是使用 ObservedObject,因为它们已经默认确认 ObservableObjects 协议。我想这就是为什么你看不到更新发生的原因。

标签: swift core-data swiftui


【解决方案1】:

不要在 Core Data 中使用 @State 来改变 Item。您应该改用@ObservedObject。更新数据后,它将刷新父视图。 请阅读这篇文章: https://purple.telstra.com/blog/swiftui---state-vs--stateobject-vs--observedobject-vs--environme

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多