SwiftUI Core Data 在 DetailView 中绑定 TextFields

Posted

技术标签:

【中文标题】SwiftUI Core Data 在 DetailView 中绑定 TextFields【英文标题】:SwiftUI Core Data Binding TextFields in DetailView 【发布时间】:2020-12-09 22:28:38 【问题描述】:

我有一个 SwiftUI 应用程序,其 SwiftUI 应用程序生命周期包括一个主从类型 从 CoreData 驱动的列表。我在 ContentView 和 NavigationLinks 中有标准列表 到细节视图。我将一个 Core Data 实体对象传递给 Detailview。

我的困难是在 DetailView 中设置与 TextFields 的绑定以进行数据输入 并用于编辑。我试图创建一个我无法工作的初始化程序。我有 只能使其与以下内容一起使用。分配初始值 尽管它确实有效,但在体内似乎并不是最好的方法。

由于核心数据实体是 ObservableObjects 我认为我应该能够 直接访问和更新绑定变量,但我找不到任何引用方式 在 ForEach 循环中绑定到 Core Data。

有没有比我下面的代码更合适的方法?

简化示例:

struct DetailView: View 

    var thing: Thing
    var count: Int

    @State var localName: String = ""
    @State private var localComment: String = ""
    @State private var localDate: Date = Date()

    //this does not work - cannot assign String? to State<String>
//    init(t: Thing) 
//        self._localName = t.name
//        self._localComment = t.comment
//        self._localDate = Date()
//    

    var body: some View 
        //this is the question - is this safe?
        DispatchQueue.main.async 
            self.localName = self.thing.name ?? "no name"
            self.localComment = self.thing.comment ?? "No Comment"
            self.localDate = self.thing.date ?? Date()
        

        return VStack 
            Text("\(thing.count)")
                .font(.title)
            Text(thing.name ?? "no what?")
            TextField("name", text: $localName)
            Text(thing.comment ?? "no comment?")
            TextField("comment", text: $localComment)
            Text("\(thing.date ?? Date())")
            //TextField("date", text: $localDate)
        .padding()
    

为了完整起见,ContentView:

struct ContentView: View 
    @Environment(\.managedObjectContext) private var viewContext
    @FetchRequest(sortDescriptors: [NSSortDescriptor(keyPath: \Thing.date, ascending: false)])
    private var things : FetchedResults<Thing>

    @State private var count: Int = 0
    @State private var coverDeletedDetail = false

    var body: some View 
        NavigationView 
            List 
                ForEach(things)  thing in
                    NavigationLink(destination: DetailView(thing: thing, count: self.count + 1)) 
                        HStack 
                            Image(systemName: "gear")
                                .resizable()
                                .frame(width: 40, height: 40)
                                .onTapGesture(count: 1, perform: 
                                    updateThing(thing)
                                )
                            Text(thing.name ?? "untitled")
                            Text("\(thing.count)")
                        
                    
                
                .onDelete(perform: deleteThings)
                if UIDevice.current.userInterfaceIdiom == .pad 
                    NavigationLink(destination: WelcomeView(), isActive: self.$coverDeletedDetail) 
                        Text("")
                    
                
            
            .navigationTitle("Thing List")
            .navigationBarItems(trailing: Button("Add Task") 
                addThing()
            )
        
    

    private func updateThing(_ thing: FetchedResults<Thing>.Element) 
        withAnimation 
            thing.name = "Updated Name"
            thing.comment = "Updated Comment"
            saveContext()
        
    

    private func deleteThings(offsets: IndexSet) 
        withAnimation 
            offsets.map  things[$0] .forEach(viewContext.delete)
            saveContext()
            self.coverDeletedDetail = true
        
    

    private func addThing() 
        withAnimation 
            let newThing = Thing(context: viewContext)
            newThing.name = "New Thing"
            newThing.comment = "New Comment"
            newThing.date = Date()
            newThing.count = Int64(self.count + 1)
            self.count = self.count + 1
            saveContext()
        
    

    func saveContext() 
        do 
            try viewContext.save()
         catch 
            print(error)
        
    

还有核心数据:

extension Thing 
    @nonobjc public class func fetchRequest() -> NSFetchRequest<Thing> 
        return NSFetchRequest<Thing>(entityName: "Thing")
    
    @NSManaged public var comment: String?
    @NSManaged public var count: Int64
    @NSManaged public var date: Date?
    @NSManaged public var name: String?


extension Thing : Identifiable 

任何指导将不胜感激。 Xcode 12.2 ios 14.2

【问题讨论】:

【参考方案1】:

你已经提到了。 CoreData 与 SwiftUI 配合得很好。

把你的东西变成 ObservableObject

@ObservedObject var thing: Thing

然后您可以将值作为绑定传递。这也适用于 ForEach

TextField("name", text: $thing.localName)

【讨论】:

谢谢。我发誓我试过了。我一定在语法上犯了错误。我会再试一次。【参考方案2】:

对于其他人 - 请注意,我必须使用上面的 Binding 扩展,因为 NSManagedObjects 是可选的。因此,正如 davidev 所说:

TextField("name", text: Binding($thing.name, "no name"))

和ObservedObject,不是Observable

【讨论】:

你是完全正确的。更正了我的答案。谢谢:)

以上是关于SwiftUI Core Data 在 DetailView 中绑定 TextFields的主要内容,如果未能解决你的问题,请参考以下文章

SWIFTUI Core Data 传递数字

Core Data - 数据传输 SwiftUI

如何让 SwiftUI 与 Core Data 一起工作(在启动项目后)?

使用 SwiftUI 2.0 和 Core Data 预填充数据

使用 Core Data 在 SwiftUI ForEach 中为 Data.Element 键入未知数

在 SwiftUI 视图中发布后台上下文 Core Data 更改而不阻塞 UI