首页 > 解决方案 > 带有 CoreData 的 TextField

问题描述

目标:使用 coredata 在具有 CRUD 功能的列表中拥有无限的 TextField。

目标示例,但没有 Coredata:

struct Item: Identifiable {
    let id = UUID()
    var title: String
}

class TestItems: ObservableObject {
    @Published var items = [Item]()
}


struct ContentView: View {
    @ObservedObject var itemGroup = TestItems()
    
    
    var body: some View {
        NavigationView{
            
            List{
                ForEach(itemGroup.items.indices, id:\.self) { index in
                    TextField("Type Stuff Here", text: $itemGroup.items[index].title)
                }
                .onDelete(perform: removeRows)
                
            }
            .navigationBarTitle("Working Example")
            .navigationBarItems(trailing: Button(action: {
                let stuff = Item(title: "")
                itemGroup.items.append(stuff)
            }, label: {
                Text("Add")
            }))
            
        }
        
    }
    
    func removeRows(at offsets: IndexSet) {
        itemGroup.items.remove(atOffsets: offsets)
    }
}

我对 Coredata 的尝试:

import SwiftUI
import CoreData

struct ContentView: View {
    @Environment(\.managedObjectContext) private var viewContext
    
    @FetchRequest(
        sortDescriptors: [
            NSSortDescriptor(keyPath: \Item.timestamp, ascending: true),
            NSSortDescriptor(keyPath: \Item.title, ascending: true)
        ],
        animation: .default)
    private var items: FetchedResults<Item>
    
    
    var body: some View {
        NavigationView{
            List {
                ForEach(items) { item in
                    TextField("Type Response Here", text: $item.title) //<-- This returns an error "cannot find item in scope"
                }
                .onDelete(perform: deleteItems)
            }
            .navigationBarTitle("CoreData")
            .navigationBarItems(trailing: Button(action: {
                addItem()
            }, label: {
                Text("Add Item")
            }))
        }
    }
    
    private func addItem() {
        withAnimation {
            let newItem = Item(context: viewContext)
            newItem.timestamp = Date()
            newItem.title = "Hello"
            
            do {
                try viewContext.save()
            } catch {
                let nsError = error as NSError
                fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
            }
        }
    }
    
    private func deleteItems(offsets: IndexSet) {
        withAnimation {
            offsets.map { items[$0] }.forEach(viewContext.delete)
            
            do {
                try viewContext.save()
            } catch {
                let nsError = error as NSError
                fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
            }
        }
    }
}

我还尝试将结构和类与 Coredata 一起使用(如我的示例),但如果可能的话,我无法找到将类保存到 Coredata 中的方法。

标签: core-dataswiftui

解决方案


您需要使用ObservedObjectCoreData 对象,为此最好为行创建单独的子视图,例如

ForEach(items) { item in
    ItemView(item: item)
}

ItemView

struct ItemView: View {
   @ObservedObject var item: Item

   var body: some View {
      // now binding over item title is provided by ObservedObject wrapper
      TextField("Type Response Here", text: $item.title)
   }
}

更新:可选属性的处理可能会有所不同,具体取决于预期的行为。这是可能的变体:

   var body: some View {
      let text = Binding(
         get: { item.title ?? "" },
         set: { item.title = $0 }
      )
      TextField("Type Response Here", text: text)
   }

注意:在字段中输入文本不会保存 CoreData 对象,因此您需要考虑将其保存在哪里,可能的变体在.onCommitfor 中TextField


推荐阅读