如何使用 swiftui 将文本字段存储到核心数据中
Posted
技术标签:
【中文标题】如何使用 swiftui 将文本字段存储到核心数据中【英文标题】:How to store textfield into core data with swiftui 【发布时间】:2020-01-06 15:57:03 【问题描述】:当输入TextField
时,我无法完成将值与核心数据一起存储,并在进入视图时再次显示的任务。有可能吗?
我需要存储name
和surname
。为此,我创建了 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 请求,但你没有使用它,这是故意的吗?
我现在没有使用它,因为首先我想关注应用程序启动的状态,name
和 surname
中没有存储任何内容。我明白你所说的保存是什么意思,但是在我发布这个问题时,我找不到更多可以使用 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. 使用预览时,我无法生成 ProfileViewreplaced 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 中的颜色类型存储到 CoreData 中?