如何使用 swiftui 将文本字段存储到核心数据中

Posted

技术标签:

【中文标题】如何使用 swiftui 将文本字段存储到核心数据中【英文标题】:How to store textfield into core data with swiftui 【发布时间】:2020-01-06 15:57:03 【问题描述】:

当输入TextField 时,我无法完成将值与核心数据一起存储,并在进入视图时再次显示的任务。有可能吗?

我需要存储namesurname。为此,我创建了 ProfileData 数据模型,但找不到任何相关信息。如何让它正常工作。

请在下面找到代码:

import SwiftUI
import CoreData

struct ProfileView: View 
    @State private var name: String = ""
    @State private var surname: String = ""
    @Environment(\.managedObjectContext) var managedObjectContext
    @FetchRequest(
        entity: ProfileData.entity(),
        sortDescriptors: [
            NSSortDescriptor(keyPath: \ProfileData.name, ascending: true),
            NSSortDescriptor(keyPath: \ProfileData.surname, ascending: true),

        ]
    ) var profile: FetchedResults<ProfileData>
    @EnvironmentObject var profile1: ProfileData

    var body: some View 
        VStack 
            HStack 
                VStack 
                    HStack 
                        Text("Meno:")
                            .font(.headline)
                            .padding()
                        TextField("", text: $name, onCommit: 
                        self.profile1.name = self.name
                        try? self.managedObjectContext.save())
                    .onAppear 
                        self.name = self.profile.name != nil ? "\(self.profile.name!)" : "Zadajte meno" //here I get error Value of type 'FetchedResults<ProfileData>' has no member 'name'
                    
                    .onDisappear 
                        self.profile1.name = self.name
                        try? self.managedObjectContext.save()
                    
                           .padding(.leading, 37)
                    
                    HStack 
                        Text("Priezvisko:")
                            .font(.headline)
                            .padding()
                        TextField("Zadajte priezvisko", text: $surname)
                    
                
            
        
        .navigationBarTitle(Text("Profil"))
    

这是我的 ProfileData+CoreClassData.swift:

import Foundation
import CoreData

@objc(ProfileData)
public class ProfileData: NSManagedObject 


这是我的 ProfileData+CoreDataProperties.swifft

//
//  ProfileData+CoreDataProperties.swift
//  UcmMap
//
//  Created by Jakub Adamec on 06/01/2020.
//  Copyright © 2020 Jakub Adamec. All rights reserved.
//
//

import Foundation
import CoreData


extension ProfileData 

    @nonobjc public class func fetchRequest() -> NSFetchRequest<ProfileData> 
        return NSFetchRequest<ProfileData>(entityName: "ProfileData")
    

    @NSManaged public var name: String?
    @NSManaged public var surname: String?


【问题讨论】:

您在此处更新profile1,但它保存在哪里?另外,你已经声明了一个 fetch 请求,但你没有使用它,这是故意的吗? 我现在没有使用它,因为首先我想关注应用程序启动的状态,namesurname 中没有存储任何内容。我明白你所说的保存是什么意思,但是在我发布这个问题时,我找不到更多可以使用 swiftui 保存带有核心数据的文本字段,所以这就是我所能准备的。 将代码更新到最新版本。 【参考方案1】:

这里是完整的代码:

import SwiftUI
import CoreData

@objc(ProfileData)
public class ProfileData: NSManagedObject, Identifiable 
    public var id = UUID()



extension ProfileData 


    @NSManaged public var name: String?
    public var wrappedName: String
        getname ?? "NoName"
        setname = newValue
    
    @NSManaged public var surname: String?
    public var wrappedSurname: String
        getsurname ?? "NoSurname"
        setsurname = newValue
    


struct ProfileView: View 
    @State private var name: String = ""
    @State private var surname: String = ""
    @Environment(\.managedObjectContext) var moc: NSManagedObjectContext // it will need you to add new examples of youre entities and save all changes
    @FetchRequest(
        entity: ProfileData.entity(),
        sortDescriptors: [
            NSSortDescriptor(keyPath: \ProfileData.name, ascending: true),
            NSSortDescriptor(keyPath: \ProfileData.surname, ascending: true),

        ]
    ) var profileList: FetchedResults<ProfileData>
    //fetchRequest is a list of all objects off type ProfileData - saved and unsaved

    var body: some View 
        NavigationView

            List
                ForEach(profileList)profile in
                    NavigationLink(destination: profileUpdateView(profile: profile))
                        Text("\(profile.wrappedName) \(profile.wrappedSurname) ")
                    
                
                HStack
                    Image(systemName: "plus.circle.fill")
                        .foregroundColor(.green)
                        .imageScale(.large)
                    Button("add a new profile")
                        let newProfile = ProfileData(context: self.moc)
                        newProfile.wrappedName = "Name"
                        newProfile.wrappedSurname = "Surname"
                        

                
             

                .navigationBarTitle(Text("Profile"))
                .navigationBarItems(trailing: Button("save")
                if self.moc.hasChanges
                    dotry self.moc.save()
                    catchprint("Cant save changes: \(error)")
                
            )

        
    

struct profileUpdateView: View 
    @ObservedObject var profile: ProfileData
    var body: some View
        VStack 
              HStack 
                  Text("Meno:")
                      .font(.headline)
                      .padding()
                TextField("Zadajte meno", text: $profile.wrappedName)
              
              HStack 
                  Text("Priezvisko:")
                      .font(.headline)
                      .padding()
                    TextField("Zadajte priezvisko", text: $profile.wrappedSurname)
              
          
    

struct ProfileView_Previews: PreviewProvider 
    static var previews: some View 
        let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
        let newProfile = ProfileData(context: context)
        newProfile.wrappedName = "Name"
        newProfile.wrappedSurname = "Surname"
        return ProfileView().environment(\.managedObjectContext, context)
    

注意几件事:

    FetchRequest 返回您定义类型的实体列表。 可能只有一项、多项,也可能没有。 如果您获取某些内容,则需要使用ForEach 循环来显示结果(大部分时间)。 为此,我向您的实体添加了id。它没有连接到CoreData,每次FetchRequest得到新的结果,id都会 改变,但没关系。它的唯一目的是让ForEach 知道,循环的哪一部分与对象完全连接。为此 ForEach 可以更改它并向您显示更新 不要忘记将数据模型中实体的codegen 属性更改为manual/none。为此,请打开 DataModel,选择您的实体并转到数据模型检查器(屏幕右侧)。如果您不这样做,编译器将在编译本身中创建这些文件,并且此类将被定义两次。 如果您想创建您的类型的新对象 - 您需要使用 MOC。它是创建NSManagedObject 类型对象的唯一方法。 在 TextField 中可以放置一个 BindableObject。您尝试为此目的使用@State,但您不需要它。可以修改普通 objects 属性就像这样:TextField("Zadajte meno", text: $profile.wrappedName) $ 符号是为了使这个属性 包裹@Binding。这意味着,在此内部所做的所有更改 View 将立即转换为该对象。 你不能只放@NSManaged属性,因为它们中的大多数都有可选类型,比如String?。我做了一个计算属性 wrappedName 在视图中简单地使用它。 我在更新视图中使用@ObservedObject 包装器。它类似于@State,但用于课程。当您想要在此对象更改时立即更新View 时,您可以使用它。它还有助于创建Binding。您的班级必须满足ObservableObject 协议要求,但NSManagedObject 已经满足,因此您无需担心。所有@NSManaged 属性都已经是@Published。 即使您使用 CoreData,也有一种使用 Canvas 的方法。只需从AppDelegate 获取上下文,创建任何测试实体。但请记住,保存的更改会添加到您的预览中。 最后,您需要使用moc.save() 保存所有更改。它可能会抛出异常,因此您必须在try-catch 中进行。而它的 检查是否确实存在未保存的更改,这是一个很好的做法。

祝你学习 SwiftUI 好运。没有太多关于使用 SwiftUI 和 CoreData 的信息。尝试在hackingwithswift 上查看,有很多有用的信息。

【讨论】:

您好@Aspid,首先感谢您的回答。我尝试实现您的代码,但出现了一些错误:1. 使用预览时,我无法生成 ProfileView replaced function 'fetchRequest()' is not marked dynamic 2. 我删除了 CoreData 数据模型,因为有一个错误,它与您的 @987654350 重复@你说的对吗? 3.我在内容视图中将此视图作为选项卡,当我模拟应用程序并单击此选项卡时,应用程序崩溃 @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate 并出现错误线程 1:致命错误:UnsafeRawBufferPointer 计数为负。这可能是因为 AppDelegate 和 SceneDelegate 包含从我将项目设置为使用 CoreData 时自动生成的方法吗? @redears 最后一个错误是您的 Xcode 无法加载您的 DataModel,因此无法访问您的实体。取回数据模型并检查是否在 AppDelegate 文件中加载了正确的模型 但是当我取回它时,它与您的@objc (ProfileData) 代码冲突,它说它是重复的 修复 'fetchRequest()' 重复 - 我刚刚删除了这个函数。我不确定,但我相信 NSManagedObject 已经具有该功能。至少这个错误只出现在 Canvas 中,并没有出现在模拟器中。如果您的“ProfileData”有重复的类定义错误 - 您忘记将 DataModel 中实体的“codeGen”属性更改为“手动/无”

以上是关于如何使用 swiftui 将文本字段存储到核心数据中的主要内容,如果未能解决你的问题,请参考以下文章

如何在 SwiftUI 中自动填充文本字段?

SwiftUI - 如何修改数组中的文本字段

如何将 SwiftUI 中的颜色类型存储到 CoreData 中?

从核心数据中获取 Double 返回到文本字段

如何检索文本字段文本并将其作为整数 32 存储在核心数据中?

如何在 iOS 中使用 Swift/SwiftUI 将我从应用数据导出的文本文件移动到应用沙箱之外的位置?