Swiftui如何从详细信息获取核心数据值到编辑视图 [英] swiftui how to fetch core data values from Detail to Edit views

查看:0
本文介绍了Swiftui如何从详细信息获取核心数据值到编辑视图的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

通过使用核心数据构建应用程序来学习迅捷;陷入了AddEdit的数据流从Detail到Edit的问题;从AddEdit到List和从List到Detail的数据流都是正常的。已在网上搜索,但没有找到有用的信息,或者我不明白。以下是这个问题的一个简化项目。它符合13.2测试版的要求,可以在模拟器上运行,但存在从详细信息编辑空白视图的问题。

视图

struct FileList: View {

    @FetchRequest(sortDescriptors: [ NSSortDescriptor(keyPath: Item.fileName, ascending: false) ], animation: .default) var items: FetchedResults<Item>
    @State private var showAdd = false

    var body: some View {
        NavigationView {
            List {
                ForEach(items) { item in
                    NavigationLink(destination: FileDetail(item: item)) {
                        Text(item.fileName ?? "").font(.headline)
                    }
                }
            }
            .navigationTitle("List")
            .navigationBarItems(trailing: Button(action: {
                showAdd = true
            }, label: { Image(systemName: "plus.circle")
            })
            .sheet(isPresented: $showAdd) {
                FileAddEdit(items: VM())
            }
            )
        }
    }
}

struct FileList_Previews: PreviewProvider {
    static let context = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
    static var previews: some View {
        FileList()
    }
}

struct FileDetail: View {
     
    @Environment(.managedObjectContext) var context
    @Environment(.presentationMode) var presentationMode
    @State var showingEdit = false
    @ObservedObject var item: Item

    var body: some View {
        VStack {
            Form {
                Text(self.item.fileName ?? "File Name")
                Button(action: {
                    showingEdit.toggle()
                }, label: {
                    title: do { Text("Edit")
                    }
                })
                .sheet(isPresented: $showingEdit) {
                    FileAddEdit(items: VM())
                }
            }
        }.navigationTitle("Detail")
    }
}

struct FileDetails_Previews: PreviewProvider {
    static let moc = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
    static var previews: some View {
        let item = Item(context: moc)
        return NavigationView {
            FileDetail(item: item)
        }
    }
}

struct FileAddEdit: View {
    
    @Environment(.managedObjectContext) var moc
    @ObservedObject var items = VM()
    
    var body: some View {
        NavigationView {
            VStack {
                Form {
                    TextField("File Name", text: $items.fileName)
                    Button(action: {
                        items.writeData(context: moc)
                    }, label: {
                    title: do { Text(items.updateFile == nil ? "Add" : "Edit")
                    }})
                }
            }
            .navigationTitle("(items.updateFile == nil ? "Add" : "Edit")")
        }
    }
}

struct FileAddEdit_Previews: PreviewProvider {
    static var previews: some View {
        FileAddEdit(items: VM())
    }
}

vm

class VM: ObservableObject {
    @Published var fileName = ""
    @Published var id = UUID()
    @Published var isNewData = false
    @Published var updateFile : Item!
    
    init() {
    }
    
    var temporaryStorage: [String] = []

    func writeData(context : NSManagedObjectContext) {
        if updateFile != nil {
            updateCurrentFile()
        } else {
            createNewFile(context: context)
        }
        do {
            try context.save()
        } catch {
            print(error.localizedDescription)
        }
    }
    
    func DetailItem(fileItem: Item){
        fileName = fileItem.fileName ?? ""
        id = fileItem.id ?? UUID()
        updateFile = fileItem
    }
    
    func EditItem(fileItem: Item){
        fileName = fileItem.fileName ?? ""
        id = fileItem.id ?? UUID()
        isNewData.toggle()
        updateFile = fileItem
    }
    
    private func createNewFile(context : NSManagedObjectContext) {
        let newFile = Item(context: context)
        newFile.fileName = fileName
        newFile.id = id
    }
    
    private func updateCurrentFile() {
        updateFile.fileName = fileName
        updateFile.id = id
    }
    
    private func resetData() {
        fileName = ""
        id = UUID()
        isNewData.toggle()
        updateFile = nil
    }
}

非常感谢您的宝贵时间和建议!

推荐答案

这是您的代码的简化版本,只需将此代码粘贴到您的项目中,并在应用程序中的body中尽可能高的位置调用YourAppParent(),因为它创建了容器。

import SwiftUI
import CoreData
//Class to hold all the Persistence methods
class CoreDataPersistence: ObservableObject{
    //Use preview context in canvas/preview
    let context = ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] == "1" ? PersistenceController.preview.container.viewContext : PersistenceController.shared.container.viewContext
    
    ///Creates an NSManagedObject of **ANY** type
    func create<T: NSManagedObject>() -> T{
        T(context: context)
        //For adding Defaults see the `extension` all the way at the bottom of this post
    }
    ///Updates an NSManagedObject of any type
    func update<T: NSManagedObject>(_ obj: T){
        //Make any changes like a last modified variable
        //Figure out the type if you want type specific changes
        if obj is FileEnt{
            //Make type specific changes
            let name = (obj as! FileEnt).fileName
            print("I'm updating FileEnt (name ?? "no name")")
        }else{
            print("I'm Something else")
        }
        
        save()
    }
    ///Creates a sample FileEnt
    //Look at the preview code for the `FileEdit` `View` to see when to use.
    func addSample() -> FileEnt{
        let sample: FileEnt = create()
        sample.fileName = "Sample"
        sample.fileDate = Date.distantFuture
        return sample
    }
    ///Deletes  an NSManagedObject of any type
    func delete(_ obj: NSManagedObject){
        context.delete(obj)
        save()
    }
    func resetStore(){
        context.rollback()
        save()
    }
    func save(){
        do{
            try context.save()
        }catch{
            print(error)
        }
    }
}
//Entry Point
struct YourAppParent: View{
    @StateObject var coreDataPersistence: CoreDataPersistence = .init()
    var body: some View{
        FileListView()
            //@FetchRequest needs it
            .environment(.managedObjectContext, coreDataPersistence.context)
            .environmentObject(coreDataPersistence)
    }
}
struct FileListView: View {
    @EnvironmentObject var persistence: CoreDataPersistence
    @FetchRequest(
        sortDescriptors: [NSSortDescriptor(keyPath: FileEnt.fileDate, ascending: true)],
        animation: .default)
    private var allFiles: FetchedResults<FileEnt>
    
    var body: some View {
        NavigationView{
            List{
                //Has to be lazy or it will create a bunch of objects because the view gets preloaded
                LazyVStack{
                    NavigationLink(destination: FileAdd(), label: {
                        Text("Add file")
                        Spacer()
                        Image(systemName: "plus")
                    })
                }
                ForEach(allFiles) { aFile in
                    NavigationLink(destination: FileDetailView(aFile: aFile)) {
                        Text(aFile.fileDate?.description ?? "no date")
                    }.swipeActions(edge: .trailing, allowsFullSwipe: true, content: {
                        Button("delete", role: .destructive, action: {
                            persistence.delete(aFile)
                        })
                    })
                }
            }
        }
    }
}
struct FileListView_Previews: PreviewProvider {
    static var previews: some View {
        YourAppParent()
//            let pers = CoreDataPersistence()
//            FileListView()
//                @FetchRequest needs it
//                .environment(.managedObjectContext, pers.context)
//                .environmentObject(pers)
    }
}
struct FileDetailView: View {
    @EnvironmentObject var persistence: CoreDataPersistence
    @ObservedObject var aFile: FileEnt
    @State var showingFileEdit: Bool = false
    
    var body: some View{
        Form {
            Text(aFile.fileName ?? "")
        }
        Button(action: {
            showingFileEdit.toggle()
        }, label: {
            Text("Edit")
        })
            .sheet(isPresented: $showingFileEdit, onDismiss: {
                //Discard any changes that were not saved
                persistence.resetStore()
            }) {
                FileEdit(aFile: aFile)
                    //sheet needs reinject
                    .environmentObject(persistence)
            }
    }
}

///A Bridge to FileEdit that creates the object to be edited
struct FileAdd:View{
    @EnvironmentObject var persistence: CoreDataPersistence
    //This will not show changes to the variables in this View
    @State var newFile: FileEnt? = nil
    var body: some View{
        Group{
            if let aFile = newFile{
                FileEdit(aFile: aFile)
            }else{
                //Likely wont ever be visible but there has to be a fallback
                ProgressView()
                    .onAppear(perform: {
                        newFile = persistence.create()
                    })
            }
        }
        .navigationBarHidden(true)
        
    }
}
struct FileEdit: View {
    @EnvironmentObject var persistence: CoreDataPersistence
    @Environment(.dismiss) var dismiss
    //This will observe changes to variables
    @ObservedObject var aFile: FileEnt
    var viewHasIssues: Bool{
        aFile.fileDate == nil || aFile.fileName == nil
    }
    var body: some View{
        Form {
            TextField("required", text: $aFile.fileName.bound)
            //DatePicker can give the impression that a date != nil
            if aFile.fileDate != nil{
                DatePicker("filing date", selection: $aFile.fileDate.bound)
            }else{
                //Likely wont ever be visible but there has to be a fallback
                ProgressView()
                    .onAppear(perform: {
                        //Set Default
                        aFile.fileDate = Date()
                    })
            }
        }
        
        Button("save", role: .none, action: {
            persistence.update(aFile)
            dismiss()
        }).disabled(viewHasIssues)
        Button("cancel", role: .destructive, action: {
            persistence.resetStore()
            dismiss()
        })
    }
}

extension Optional where Wrapped == String {
    var _bound: String? {
        get {
            return self
        }
        set {
            self = newValue
        }
    }
    var bound: String {
        get {
            return _bound ?? ""
        }
        set {
            _bound = newValue
        }
    }
    
}
extension Optional where Wrapped == Date {
    var _bound: Date? {
        get {
            return self
        }
        set {
            self = newValue
        }
    }
    public var bound: Date {
        get {
            return _bound ?? Date.distantPast
        }
        set {
            _bound = newValue
        }
    }
}

要添加需要对象的预览,您可以将此代码与新的CoreDataPersistence

一起使用
///How to create a preview that requires a CoreData object.
struct FileEdit_Previews: PreviewProvider {
    static let pers = CoreDataPersistence()
    static var previews: some View {
        VStack{
            FileEdit(aFile: pers.addSample()).environmentObject(pers)
        }
    }
}

由于create()现在是泛型的,您可以使用实体的extension将默认值添加到变量。

extension FileEnt{ 
    public override func awakeFromInsert() {
        //Set defaults here
        self.fileName = ""
        self.fileDate = Date()
    }
}

这篇关于Swiftui如何从详细信息获取核心数据值到编辑视图的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆