Core data 创建一个NSManagedObject并注入到SwiftUI视图中,该视图将自动添加到导航堆栈中
我的主应用程序屏幕显示对象列表(本例中的患者)。我想允许用户通过点击工具栏按钮添加新患者,该按钮允许用户添加相关详细信息 实现编辑视图非常简单,编辑现有患者也不会有任何问题。在列表视图中添加新患者也很容易,无需任何编辑视图的自动模式显示或导航。当然,缺点是用户需要手动执行一系列额外的步骤来编辑新添加的对象,这对用户不是很友好 如果我使用MVVM,这不是问题,但我正在尝试使用苹果自己的“推荐”方法,在主运行中心周围使用属性包装等。因为在视图初始化完成之前,我无法访问主运行中心,所以最简单的方法(如果没有提供视图,则创建新患者)是不可能的。当前的策略(在父视图中创建新对象)导致状态不一致,并且每当您尝试编辑子视图中的字段时,就会触发NavigationLink,从而导致一些非常奇怪的行为 我确信我把这件事弄得不必要的复杂,并且仍然停留在一种必须的心态中,但是一些指导会有所帮助Core data 创建一个NSManagedObject并注入到SwiftUI视图中,该视图将自动添加到导航堆栈中,core-data,swiftui,Core Data,Swiftui,我的主应用程序屏幕显示对象列表(本例中的患者)。我想允许用户通过点击工具栏按钮添加新患者,该按钮允许用户添加相关详细信息 实现编辑视图非常简单,编辑现有患者也不会有任何问题。在列表视图中添加新患者也很容易,无需任何编辑视图的自动模式显示或导航。当然,缺点是用户需要手动执行一系列额外的步骤来编辑新添加的对象,这对用户不是很友好 如果我使用MVVM,这不是问题,但我正在尝试使用苹果自己的“推荐”方法,在主运行中心周围使用属性包装等。因为在视图初始化完成之前,我无法访问主运行中心,所以最简单的方法(如
struct PatientsView: View {
@EnvironmentObject var dataController: DataController
@Environment(\.managedObjectContext) var managedObjectContext
static let tag = "patients"
@State private var navLinkTag: Int? = 0
let patients: FetchRequest<Patient>
init() {
patients = FetchRequest(entity: Patient.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \Patient.creationDate, ascending: false)])
}
var body: some View {
NavigationView {
List {
ForEach(patients.wrappedValue) { patient in
PatientRow(patient: patient)
}
.onDelete { offsets in
for offset in offsets {
let item = patients.wrappedValue[offset]
dataController.delete(item)
}
}
}
.navigationTitle("Patients")
.toolbar {
ToolbarItem {
Button(action: {
navLinkTag = 1
}, label: {
Image(systemName: "plus")
})
.background(
NavigationLink(
destination: EditPatientView(patient: newPatient()),
tag: 1,
selection: $navLinkTag,
label: { EmptyView() })
)
}
}
}
}
private func newPatient() -> Patient {
let patient = Patient(context: managedObjectContext)
patient.creationDate = Date()
patient.name = "New patient"
dataController.save()
return patient
}
}
struct PatientRow: View {
@ObservedObject var patient: Patient
var body: some View {
HStack {
NavigationLink(destination: PatientActivityView(patient: patient)) {
VStack {
HStack {
Text(patient.patientName)
Text(patient.patientOwnerName)
.bold()
Spacer()
}
HStack {
Text(patient.patientSpecies)
Text(patient.patientSex)
Text(patient.age)
Spacer()
}
.font(.caption)
}
}
}
}
}
struct EditPatientView: View {
@EnvironmentObject var dataController: DataController
@Environment(\.managedObjectContext) var managedObjectContext
@State var owner: String
@State var name: String
@State var species: String
@State var dob: Date
@State var sex: String
let patient: Patient
init(patient: Patient) {
self.patient = patient
_owner = State(wrappedValue: self.patient.patientOwnerName)
_name = State(wrappedValue: self.patient.patientName)
_species = State(wrappedValue: self.patient.patientSpecies)
_sex = State(wrappedValue: self.patient.patientSex)
_dob = State(wrappedValue: self.patient.patientDateOfBirth)
}
var body: some View {
Form {
Section(header: Text("Identity")) {
TextField("Name", text: $name.onChange(update))
TextField("Owner", text: $owner.onChange(update))
}
Section(header: Text("Signalment")) {
TextField("Species", text: $species.onChange(update))
TextField("Sex", text: $sex.onChange(update))
DatePicker("Date of birth",
selection: $dob.onChange(update),
in: ...Date(),
displayedComponents: .date)
.datePickerStyle(CompactDatePickerStyle()) }
}
.navigationTitle("Edit Patient")
}
private func update() {
patient.name = name
patient.owner = owner
patient.species = species
patient.sex = sex
patient.dateOfBirth = dob
}
}
struct PatientsView:View{
@EnvironmentObject变量dataController:dataController
@环境(\.managedObjectContext)变量managedObjectContext
静态let tag=“患者”
@国家私有变量navLinkTag:Int?=0
让患者:请求
init(){
patients=FetchRequest(实体:Patient.entity(),sortDescriptors:[NSSortDescriptor(键路径:\Patient.creationDate,升序:false)])
}
var body:一些观点{
导航视图{
名单{
ForEach(patients.wrappedValue){patient in
PatientRow(患者:患者)
}
.onDelete{中的偏移量
用于偏移中的偏移{
let item=patients.wrappedValue[offset]
dataController.delete(项目)
}
}
}
.navigationTitle(“患者”)
.工具栏{
工具栏项{
按钮(操作:{
navLinkTag=1
},标签:{
图像(系统名称:“plus”)
})
.背景(
导航链接(
目标:EditPatientView(患者:newPatient()),
标签:1,
选择:$navLinkTag,
标签:{EmptyView()})
)
}
}
}
}
private func newPatient()->Patient{
让患者=患者(上下文:managedObjectContext)
patient.creationDate=日期()
patient.name=“新患者”
dataController.save()
返回病人
}
}
结构PatientRow:视图{
@观察对象变量患者:患者
var body:一些观点{
HStack{
导航链接(目的地:PatientActivityView(患者:患者)){
VStack{
HStack{
文本(patient.patientName)
文本(patient.patientTownerName)
.bold()
垫片()
}
HStack{
文本(患者。患者种类)
文本(patient.patientSex)
文本(患者年龄)
垫片()
}
.font(.caption)
}
}
}
}
}
结构EditPatientView:视图{
@EnvironmentObject变量dataController:dataController
@环境(\.managedObjectContext)变量managedObjectContext
@状态变量所有者:字符串
@状态变量名称:String
@状态变量种类:字符串
@状态变量dob:日期
@状态变量性别:字符串
让病人:病人
初始(患者:患者){
病人
_所有者=状态(wrappedValue:self.patient.patientTownerName)
_name=State(wrappedValue:self.patient.patientName)
_种类=状态(wrappedValue:self.patient.patientSpecies)
_性别=状态(wrappedValue:self.patient.patientSex)
_dob=状态(wrappedValue:self.patient.patientDateOfBirth)
}
var body:一些观点{
形式{
节(标题:文本(“标识”)){
TextField(“Name”,text:$Name.onChange(更新))
TextField(“所有者”,text:$Owner.onChange(更新))
}
章节(标题:文本(“信号”)){
TextField(“物种”,text:$Species.onChange(更新))
TextField(“Sex”,text:$Sex.onChange(更新))
日期选择器(“出生日期”,
选择:$dob.onChange(更新),
在:…日期(),
displayedComponents:。日期)
.datePickerStyle(CompactDatePickerStyle())}
}
.navigationTitle(“编辑患者”)
}
私有函数更新(){
patient.name=姓名
patient.owner=所有者
种类
病人性别
患者出生日期=出生日期
}
}
您的问题在于新的托管对象和编辑对象的处理方式不同。我有不同的看法:
struct EditPatientView: View {
@EnvironmentObject var dataController: DataController
@Environment(\.managedObjectContext) var managedObjectContext
@State private var owner: String
@State private var name: String
@State private var species: String
@State private var dob: Date
@State private var sex: String
let patient: Patient
init(patient: Patient) {
self.patient = patient
_owner = State(wrappedValue: self.patient.patientOwnerName)
_name = State(wrappedValue: self.patient.patientName)
_species = State(wrappedValue: self.patient.patientSpecies)
_sex = State(wrappedValue: self.patient.patientSex)
_dob = State(wrappedValue: self.patient.patientDateOfBirth)
}
var body: some View {
Form {
Section(header: Text("Identity")) {
TextField("Name", text: $name.onChange(update))
TextField("Owner", text: $owner.onChange(update))
}
Section(header: Text("Signalment")) {
TextField("Species", text: $species.onChange(update))
TextField("Sex", text: $sex.onChange(update))
DatePicker("Date of birth",
selection: $dob.onChange(update),
in: ...Date(),
displayedComponents: .date)
.datePickerStyle(CompactDatePickerStyle()) }
}
.navigationTitle("Edit Patient")
}
private func update() {
patient.setValue(self.name, forKey: "patientName")
patient.setValue(self.owner, forKey: "patientOwnerName")
patient.setValue(self.species, forKey: "patientSpecies")
patient.setValue(self.sex, forKey: "patientSex")
patient.setValue(self.dob, forKey: "patientDateOfBirth")
do {
try self.managedObjectContext.save()
} catch {
//Handle any error
}
}
}
对于新患者:
struct NewPatientView: View {
@EnvironmentObject var dataController: DataController
@Environment(\.managedObjectContext) var managedObjectContext
@State private var owner: String = ""
@State private var name: String = ""
@State private var species: String = ""
@State private var dob: Date = Date()
@State private var sex: String = ""
var body: some View {
Form {
Section(header: Text("Identity")) {
TextField("Name", text: $name)
TextField("Owner", text: $owner)
}
Section(header: Text("Signalment")) {
TextField("Species", text: $species)
TextField("Sex", text: $sex)
DatePicker("Date of birth",
selection: $dob,
in: ...Date(),
displayedComponents: .date)
.datePickerStyle(CompactDatePickerStyle())
}
Button(action: {
self.save()
}) {
Text("Save")
}
}
.navigationTitle("New Patient")
}
private func save() {
let patient = Patient(context: self.managedObjectContext)
patient.name = name
patient.owner = owner
patient.species = species
patient.sex = sex
patient.dateOfBirth = dob
do {
try self.managedObjectContext.save()
} catch {
//Handle any error
}
}
}
我也会考虑让它自己的实体通过一种关系联系到每一个动物。否则,如果所有者有mo,您将不断复制他们
import SwiftUI
struct PatientsView: View {
@EnvironmentObject var dataController: DataController
@Environment(\.managedObjectContext) var managedObjectContext
@State private var showEditPatient = false
@State private var newPatient: Patient? = nil /// need to use @State as the PatientView FetchedResult will change when Patient is added or mutated causing the hierachy to be discarded and refreshed. A "normal" property will be re-created leading to multiple new Patient instances and warnings about state amendment during a view update
private let patients: FetchRequest<Patient>
init() {
patients = FetchRequest(entity: Patient.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \Patient.creationDate, ascending: false)])
}
var body: some View {
let createNewPatient = Binding<Bool>( /// A custom binding seems to be the best way of creating new Patient instances only when required
get: { return showEditPatient },
set: { newValue in
if !newValue {
newPatient = nil /// EditPatient view has been dismissed so discard the Patient
} else {
if newPatient == nil { newPatient = createPatient() } /// New Patient required
}
showEditPatient = newValue
}
)
return NavigationView {
List {
ForEach(patients.wrappedValue) { patient in
PatientRow(patient: patient)
}
.onDelete { offsets in
for offset in offsets {
let item = patients.wrappedValue[offset]
dataController.delete(item)
}
}
}
.navigationTitle("Patients")
.toolbar {
ToolbarItem {
Button(action: {
createNewPatient.wrappedValue = true
}, label: {
Image(systemName: "plus")
})
}
}
.sheet(isPresented: $showEditPatient,
onDismiss: { createNewPatient.wrappedValue = false }) {
EditPatientView(patient: newPatient!) /// Patient should never be nil - force unwrap will catch unexpected code path
}
}
}
private func createPatient() -> Patient {
let patient = Patient(context: managedObjectContext)
patient.creationDate = Date()
patient.name = "New patient"
dataController.save()
return patient
}
}
struct PatientsView_Previews: PreviewProvider {
static let dataController = DataController.preview
static var previews: some View {
PatientsView()
.environment(\.managedObjectContext, dataController.container.viewContext)
.environmentObject(dataController)
}
}