如何跨视图传递 CoreData 对象

Posted

技术标签:

【中文标题】如何跨视图传递 CoreData 对象【英文标题】:How to pass CoreData Object across views 【发布时间】:2021-03-17 19:26:14 【问题描述】:

因此,我正在开发自己的 SwiftUI 项目,试图弄清楚如何将 SwiftUI 与 CoreData 集成以实现数据持久性。我遇到了一些麻烦,我试图弄清楚如何获取我在“注册视图”中创建的用户实体或从“登录视图”中创建的现有用户,以及如何将该用户对象传递到ContentView,这是用户登录后出现的第一个视图。

首先,我不确定是否应该在 ContentView 中使用 @Binding 属性包装器来捕获从“登录视图”或“注册视图”传入的对象,或者是否应该只使用一个常量但没有包装器(我猜@Binding 是这里的方法,但我想对此进行一些澄清)。

其次,我想弄清楚为什么添加 @Binding 包装器会强制预览在输入 selectedUser 属性之前添加逗号?另外,我将如何解决预览错误?

第三,我对应该在何时何地导入 CoreData 感到有些困惑。我什至必须导入它吗?如果是这样,在哪些情况下我必须导入 CoreData?

我知道这是很多菜鸟问题,但我是菜鸟,我一直在寻找答案,无论我在哪里,都会得到不同的答案。非常感激。谢谢!

import SwiftUI

struct ContentView: View 
            
    @Environment(\.managedObjectContext) var moc
    
    @Binding var selectedUser : User //Assign logged in user to this variable...
    
    
    var body: some View 
        
            VStack
                Spacer()
                VStack(spacing: 50)
                    
                    NavigationLink(destination: SupportView())
                        awButton(content: "Request Support", backColor: Color(#colorLiteral(red: 0, green: 0.723585546, blue: 0.9907287955, alpha: 1)))
                            .shadow(color: Color.primary.opacity(0.5), radius: 20, x: 0, y: 20)
                            .rotation3DEffect(Angle(degrees:10), axis: (x: 10.0, y: 0, z: 0))
                    

                    NavigationLink(destination: QuoteView())
                        awButton(content: "Request Quote", backColor: Color(#colorLiteral(red: 0.9372549057, green: 0.3490196168, blue: 0.1921568662, alpha: 1)))
                            .shadow(color: Color.primary.opacity(0.5), radius: 20, x: 0, y: 20)
                            .rotation3DEffect(Angle(degrees:10), axis: (x: 10.0, y: 0, z: 0))
                    

                    NavigationLink(destination: TicketView())
                        awButton(content: "Ticket Status", backColor: Color(#colorLiteral(red: 0.4666666687, green: 0.7647058964, blue: 0.2666666806, alpha: 1)))
                            .shadow(color: Color.primary.opacity(0.5), radius: 20, x: 0, y: 20)
                            .rotation3DEffect(Angle(degrees:10), axis: (x: 10.0, y: 0, z: 0))
                    
                
                Spacer()
            
            .navigationTitle("AccessWeb")
            .navigationBarItems(trailing: HStack
                Image(systemName: "bell")
                    .font(.system(size: 30))
                Image(systemName:"person.circle")
                    .font(.system(size: 30))
                Text("Tester")
                    .font(.system(size: 20))
            )
       


struct ContentView_Previews: PreviewProvider 

    static var previews: some View 
        
        ContentView(, selectedUser: <#Binding<User?>#>)
    




import SwiftUI
import CoreData

struct SignUpView: View 

@State private var name: String = ""
@State private var company: String = ""
@State private var user: String = ""
@State private var pass: String = ""
@State private var photo: Image?

@State private var showingImagePicker = false
@State private var inputImage: UIImage?

@State private var isSignUpValid: Bool = false
@State private var shoudlShowSignUpAlert: Bool = false
    
@Environment(\.managedObjectContext) var moc

var body: some View 
    
    VStack

        TextField("Company", text: $company)
            .padding(.leading)
        Divider()
            .padding(.bottom)
        
        TextField("First and Last Name", text: $name)
            .padding(.leading)
        Divider()
            .padding(.bottom, 30)
        
        TextField("Username", text: $user)
            .padding(.leading)
        Divider()
            .padding(.bottom)
        
        SecureField("Password", text: $pass)
            .padding(.leading)
            
        Divider()
            .padding(.bottom, 50)
        
        ZStack
            Circle()
                .fill(Color.secondary)
                .frame(width: 200, height: 200)
                
            if photo != nil 
                photo?
                    .resizable()
                    .scaledToFit()
                    .clipShape(Circle())
                    .frame(width: 300, height: 300)
             else 
                Text("Tap to select a photo")
                    .foregroundColor(.white)
                    .font(.headline)
            
        
        .onTapGesture 
            self.showingImagePicker = true
        
        .padding(.bottom, 50)
        
        
        NavigationLink(
            destination: ContentView(),
            isActive: self.$isSignUpValid)
            Text("Sign Up")
                .foregroundColor(.blue)
                .onTapGesture 

                    //Pass the appropriate User data onto the ContentView...
                    
                    //TriggerLogin
                    let isLoginValid = self.user == "Tester" && self.pass == "Test1234"
                    
                    if isLoginValid 
                        self.isSignUpValid = true //trigger NavLink
                        
                        let newUser = User(context: self.moc)
                        newUser.company = company
                        newUser.name = name
                        newUser.username = user
                        newUser.password = pass
                        newUser.photo = coreDataObjectFromImages(image: (inputImage ?? UIImage(systemName:"person.circle"))!)

                        do 
                            try self.moc.save()
                         catch 
                            print(error)
                        

                     else 
                        self.shoudlShowSignUpAlert = true
                    


                
        
        .frame(width: 300, height: 50)
        .background(Color.green)
        .clipShape(RoundedRectangle(cornerRadius: 20, style: .continuous))
    
    .sheet(isPresented: $showingImagePicker, onDismiss: loadImage)
        ImagePicker(image: self.$inputImage)
    
    .navigationTitle("Sign Up")
    .alert(isPresented: $shoudlShowSignUpAlert, content: 
        Alert(title: Text("Fill it out correctly!"))
    )

【问题讨论】:

由于您的问题如此广泛,很难回答,但如果您想知道如何传递 CoreData 对象并共享它们,Apple SwiftUI tutorials 是一个很好的资源。 CoreData 对象是一个ObservableObject,所以无论您在哪里看到可以是CoreData 对象的ObservedObjectEnvironmentObjectStateObject 【参考方案1】:

对于你的第一个问题:这里不需要使用绑定。你只需在你的 ContentView 中声明一个 User 类型的变量。

CoreData 和 SwiftUI 结合在一起如此强大的原因在于 ManagedObjects 符合 ObservableObjects。所以你可以在你的视图中将它们声明为ObservedObject

import SwiftUI

struct ContentView: View 
            
    @Environment(\.managedObjectContext) var moc
    
    //@Binding var selectedUser : User

    @ObservedObject var selectedUser: User //<< here declare it 

第三个问题:由于 CoreData 是一个框架,就像 ios 中的其他框架一样,当你使用它的函数、类、结构时,你必须导入它。你可以看看here

【讨论】:

谢谢,这有帮助!知道为什么我在更改为 @ObservedObject 后在 ContentView 的预览中看到逗号吗? (ContentView(, selectedUser: )) 另外,由于预览要求我包含一个用户对象,我猜我也必须在预览中生成一个示例对象?

以上是关于如何跨视图传递 CoreData 对象的主要内容,如果未能解决你的问题,请参考以下文章

在没有 managedObjectContext 的视图之间传递 CoreData 对象

在 CoreData 中传递对象

如何将集合视图数据传递给新的视图控制器

跨线程传递临时 NSManagedObject

无法在 SwiftUI 中使用 ForEach 和 CoreData 将数据正确传递给模态演示

如何将 ManagedObjectContext 快速传递给根视图控制器