swift - 如何使用 swiftui 将文本字段存储到核心数据中
问题描述
我无法完成在输入时将值与核心数据一起存储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.swift
//
// 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?
}
解决方案
这是完整的代码:
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{
get{name ?? "NoName"}
set{name = newValue}
}
@NSManaged public var surname: String?
public var wrappedSurname: String{
get{surname ?? "NoSurname"}
set{surname = 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{
do{try self.moc.save()}
catch{print("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,但您不需要它。你可以像这样修改普通对象的属性:
TextField("Zadajte meno", text: $profile.wrappedName)
符号$
是让这个属性被包裹@Binding
。这意味着,在其中所做的所有更改都View
将立即转换为该对象。 - 你不能只放
@NSManaged
属性,因为它们中的大多数都有像 String? 这样的可选类型。我创建了一个计算属性wrappedName
,以便在 Views 中简单地使用它。 - 我在更新视图中使用
@ObservedObject
包装器。它喜欢@State
,但对于类。当您想要View
在此对象更改时立即更新时使用它。它还有助于创建一个Binding
. 你的类必须满足ObservableObject
协议要求,但NSManagedObject
已经满足了,所以你不必担心。所有@NSManaged
属性都已经@Published
. - 即使您使用 CoreData,也有一种使用 Canvas 的方法。只需从中获取上下文
AppDelegate
,创建任何测试实体。但请记住,保存的更改将添加到您的预览中。 - 最后,您需要使用
moc.save()
. 它可以抛出异常,所以你必须在try-catch
. 如果确实有未保存的更改,检查它是一个很好的做法。
祝你学习 SwiftUI 好运。没有太多关于使用 SwiftUI 和 CoreData 的信息。尝试在 hackingwithswift上查看它,有很多有用的信息。
推荐阅读
- c# - (反)将子 JSON 属性序列化为类属性
- nginx - 阻止动态 Nginx 位置
- java - 如何从Java中的多个嵌套json中获取键值
- javascript - 表单验证未显示错误消息
- html - 您如何使用 flexbox 制作 plus 布局?
- java - 如何使用 Springboot thymeleaf 添加两个数字
- python-3.x - 如何在 tensorflow 版本 2 中转换以下内容
- css - 使用 Alpha 通道获得相似的颜色
- dataweave - Data weave 2.0 中的日期转换 YYYYMMDD 到 YYYY-MM-DD
- python - 我的 Python 输出重复了数百次