【问题标题】:Use the same view for adding and editing CoreData objects使用相同的视图添加和编辑 CoreData 对象
【发布时间】:2021-09-25 22:45:57
【问题描述】:

我正在开发一个跟踪人们用药情况的 iOS 应用程序,我有一个添加视图和一个编辑视图,除了在我的编辑视图上我使用 .onAppear 加载所有药物数据之外,两者看起来几乎相同使用let medication: Medication

进入现有药物的领域

我的表单看起来像这样:

        Form {
            Group {
                TextField("Medication name", text: $name).disableAutocorrection(true)
                TextField("Remaining quantity", text: $remainingQuantity).keyboardType(.numberPad)
                TextField("Box quantity", text: $boxQuantity).keyboardType(.numberPad)
                DatePicker("Date", selection: $date, in: Date()...).datePickerStyle(GraphicalDatePickerStyle()) 
                Picker(selection: $repeatPeriod, label: Text("Repeating")) {
                        ForEach(RepeatPeriod.periods, id: \.self) { periods in
                            Text(periods).tag(periods)  
                }
            .onAppear {
                if pickerView {
                    self.name = self.medication.name != nil ? "\(self.medication.name!)" : ""
                    self.remainingQuantity = (self.medication.remainingQuantity != 0) ? "\(self.medication.remainingQuantity)" : ""
                    self.boxQuantity = (self.medication.boxQuantity != 0) ? "\(self.medication.boxQuantity)" : ""
                    self.date = self.medication.date ?? Date()
                    self.repeatPeriod = self.medication.repeatPeriod ?? "Nunca"
                    self.notes = self.medication.notes != nil ? "\(self.medication.notes!)" : ""
                }
            }
        }

我曾想过使用像 isEditMode 这样的绑定变量,它工作正常,但在调用不提供对象的添加视图时遇到了一些与 moc 对象相关的问题。

这是我的 editView 预览的样子

struct EditMedicationSwiftUIView_Previews: PreviewProvider {
static let moc = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)

static var previews: some View {
    let medication = Medication(context: moc)
    
    return NavigationView {
        EditMedicationSwiftUIView(medication: medication)
    }
    
    
}

}

有什么建议吗?

【问题讨论】:

  • 欢迎来到 SO - 请使用 tour 并阅读 How to Ask 以改进、编辑和格式化您的问题。如果没有Minimal Reproducible Example,就无法帮助您进行故障排除。
  • 表单体验,添加编辑功能比仅渲染不可变数据复杂约 5 倍。因此,我建议使用有可能干净地解决此类问题的适当模式:MVVM、MVI 等 - 包括将所有逻辑移动到“ViewModel”,并且所有“服务”都将通过抽象访问层,即“模型”或“商店”。因此,您的视图中没有“FetchRequest” - 只是被渲染的简单的不可变数据。

标签: swift core-data swiftui swiftui-navigationlink


【解决方案1】:

这是我认为您正在尝试做的简化版本。它使用来自 SwiftUI 示例项目的代码。只需使用 CoreData 创建一个 Xcode SwiftUI 项目。

import SwiftUI
import CoreData
//Standard List Screen where you can select an item to see/edit and you find a button to add
struct ReusableParentView: View {
    @Environment(\.managedObjectContext) private var viewContext
    
    @FetchRequest(
        sortDescriptors: [NSSortDescriptor(keyPath: \Item.timestamp, ascending: true)],
        animation: .default)
    private var items: FetchedResults<Item>
    //Keeps work out of the Views so it can be reused
    @StateObject var vm: ReusableParentViewModel = ReusableParentViewModel()
    var body: some View {
        NavigationView{
            List{
                ForEach(items) { item in
                    NavigationLink {
                        //This is the same view as the sheet but witht he item passed fromt he list
                        ReusableItemView(item: item)
                        
                    } label: {
                        VStack{
                            Text(item.timestamp.bound, formatter: itemFormatter)
                            Text(item.hasChanges.description)
                        }
                        
                    }
                }.onDelete(perform: { indexSet in
                    for idx in indexSet{
                        vm.deleteItem(item: items[idx], moc: viewContext)
                    }
                })
            }
            //Show sheet to add new item
            .sheet(item: $vm.newItem, onDismiss: {
                
                vm.saveContext(moc: viewContext)
                //You can also cancel/get rid of the new item/changes if the user doesn't save
                //vm.cancelAddItem(moc: viewContext)
            }, content: { newItem in
                NavigationView{
                    ReusableItemView(item: newItem)
                    
                }
                //Inject the VM the children Views have access to the functions
                .environmentObject(vm)
            })
            
            .toolbar(content: {
                ToolbarItem(placement: .automatic, content: {
                    //Trigger new item sheet
                    Button(action: {
                        vm.addItem(moc: viewContext)
                    }, label: {
                        Image(systemName: "plus")
                    })
                })
            })
        }
        //Inject the VM the children Views have access to the functions
        .environmentObject(vm)
    }
    private let itemFormatter: DateFormatter = {
        let formatter = DateFormatter()
        formatter.dateStyle = .short
        formatter.timeStyle = .medium
        return formatter
    }()
}
//The Item's View
struct ReusableItemView: View {
    //All CoreData objects are ObservableObjects to see changes you have to wrap them in this
    @ObservedObject var item: Item
    @Environment(\.editMode) var editMode
    var body: some View {
        VStack{
            if editMode?.wrappedValue == .active{
                EditItemView(item: item)
            }else{
                ShowItemView(item: item)
            }
        }
        .toolbar(content: {
            ToolbarItem(placement: .automatic, content: {
                //If you want to edit this info just press this button
                Button(editMode?.wrappedValue == .active ? "done": "edit"){
                    if editMode?.wrappedValue == .active{
                        editMode?.wrappedValue = .inactive
                    }else{
                        editMode?.wrappedValue = .active
                    }
                }
            })
        })
    }
}
//The View to just show the items info
struct ShowItemView: View {
    //All CoreData objects are ObservableObjects to see changes you have to wrap them in this
    @ObservedObject var item: Item
    var body: some View {
        if item.timestamp != nil{
            Text("Item at \(item.timestamp!)")
        }else{
            Text("nothing to show")
        }
    }
}
//The View to edit the item's info
struct EditItemView: View {
    @Environment(\.managedObjectContext) private var viewContext
    @EnvironmentObject var vm: ReusableParentViewModel
    @Environment(\.editMode) var editMode
    //All CoreData objects are ObservableObjects to see changes you have to wrap them in this
    @ObservedObject var item: Item
    var body: some View {
        DatePicker("timestamp", selection: $item.timestamp.bound).datePickerStyle(GraphicalDatePickerStyle())
    }
}
struct ReusableParentView_Previews: PreviewProvider {
    static var previews: some View {
        ReusableParentView().environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
    }
}
class ReusableParentViewModel: ObservableObject{
    //Can be used to show a sheet when a new item is created
    @Published var newItem: Item? = nil
    //If you dont want to create a CoreData item immediatly just present a sheet with the AddItemView in it
    @Published var presentAddSheet: Bool = false
    func addItem(moc: NSManagedObjectContext) -> Item{
        //You should never create an ObservableObject inside a SwiftUI View unless it is using @StateObject which doesn't apply to a CoreData object
        let temp = Item(context: moc)
        temp.timestamp = Date()
        //Sets the newItem variable
        newItem = temp
        //And returns the new item for other uses
        return temp
    }
    func cancelAddItem(moc: NSManagedObjectContext){
        rollbackChagnes(moc: moc)
        newItem = nil
    }
    func rollbackChagnes(moc: NSManagedObjectContext){
        moc.rollback()
    }
    func deleteItem(item: Item, moc: NSManagedObjectContext){
        moc.delete(item)
        saveContext(moc: moc)
    }
    func saveContext(moc: NSManagedObjectContext){
        do{
            try moc.save()
        }catch{
            print(error)
        }
    }
}

如果由于某种原因你不想提前创建一个 CoreData 对象,这似乎是你正在做的事情,你总是可以创建临时变量并制作一个可共享的可编辑视图,每个都包含 @Binding您要编辑的变量。

//The View to Add the item's info, you can show this anywhere.
struct AddItemView: View {
    @Environment(\.managedObjectContext) private var viewContext
    @EnvironmentObject var vm: ReusableParentViewModel
    
    //These can be temporary variables
    @State var tempTimestamp: Date = Date()
    var body: some View {
        EditableItemView(timestamp: $tempTimestamp)
            .toolbar(content: {
                ToolbarItem(placement: .navigationBarLeading, content: {
                    //Create and save the item
                    Button("save"){
                        let new = vm.addItem(moc: viewContext)
                        new.timestamp = tempTimestamp
                        vm.saveContext(moc: viewContext)
                    }
                })
            })
    }
}
//The View to edit the item's info
struct EditItemView: View {
    @EnvironmentObject var vm: ReusableParentViewModel
    @Environment(\.managedObjectContext) private var viewContext
    @ObservedObject var item: Item
    var body: some View {
        VStack{
        EditableItemView(timestamp: $item.timestamp.bound)
            .onDisappear(perform: {
                vm.rollbackChagnes(moc: viewContext)
            })
            //Just save the item
            Button("save"){
                vm.saveContext(moc: viewContext)
            }
        }
    }
}
//The View to edit the item's info
struct EditableItemView: View {
    @Environment(\.managedObjectContext) private var viewContext
    @EnvironmentObject var vm: ReusableParentViewModel
    //All CoreData objects are ObservableObjects to see changes you have to wrap them in this
    @Binding var timestamp: Date
    var body: some View {
        DatePicker("timestamp", selection: $timestamp).datePickerStyle(GraphicalDatePickerStyle())
    }
}

【讨论】:

    猜你喜欢
    • 2012-05-27
    • 2011-01-24
    • 1970-01-01
    • 2015-07-10
    • 1970-01-01
    • 1970-01-01
    • 2021-05-22
    • 1970-01-01
    • 2018-05-29
    相关资源
    最近更新 更多