SwiftUI ImagePicker 保存(图像-> UIImage--> 数据)到核心数据

Posted

技术标签:

【中文标题】SwiftUI ImagePicker 保存(图像-> UIImage--> 数据)到核心数据【英文标题】:SwiftUI ImagePicker save (Image -> UIImage --> Data) to Core Data 【发布时间】:2020-10-31 00:23:18 【问题描述】:

大家好,我有点沮丧,所以我希望能得到一些帮助。我的项目在 SwiftUI 中。我想使用图像选择器将图像保存到核心数据。我实现了让 ImagePicker 工作,但我努力转换 Image --> UIImage --> Data 然后保存它。项目运行没有错误,但它不保存图像。要知道问题在哪里,我实施了 3 个打印语句。 image=给了一个值,UIImage(inputImage)=nil,Data当然是nil。

import SwiftUI


struct AddNewBean: View 
    
    @Environment(\.presentationMode) var presentationMode
    @Environment(\.managedObjectContext) var moc
    @FetchRequest(entity: Bean.entity(), sortDescriptors: []) var beans: FetchedResults<Bean>
    @State var BeanRoaster: String = ""
    @State var BeanName: String = ""
    @State var BeanStyle: String = "Dark"
    
    @State private var RoastDate = Date()
        var dateFormatter: DateFormatter 
        let formatter = DateFormatter()
        formatter.dateStyle = .long
        return formatter 
    
    @State private var showImagePicker : Bool = false
    @State private var image : Image?
    @State private var inputImage: UIImage?
    @State var imageAlt: Data = .init(count: 0)
    
    let RStyles = ["Dark", "Medium", "Light"]
    
    func loadImage() 
        guard let inputImage = inputImage else  return 
        image = Image(uiImage: inputImage)
    
    
    var body: some View 
        
        NavigationView 
            VStack 
                Form 
                    VStack 
                        image?.resizable().scaledToFit().aspectRatio(contentMode: .fit)
                        HStack 
                            Spacer()
                            Button("Open Camera")
                                self.showImagePicker = true
                            .padding(5)
                                .foregroundColor(Color.white)
                                .background(Color.accentColor)
                                .cornerRadius(10)
                            Spacer()
                        .sheet(isPresented: self.$showImagePicker, onDismiss: loadImage)
                            PhotoCaptureView(showImagePicker: self.$showImagePicker, image: self.$image)
                        
                    
                    TextField("Röster", text: $BeanRoaster)
                    TextField("Name der Bohne", text: $BeanName)
                    Picker("Roestung", selection: $BeanStyle) 
                        ForEach(RStyles, id: \.self) 
                            Text($0)
                        
                    
                    DatePicker(selection: $RoastDate, in: ...Date(), displayedComponents: .date) Text("Röstdatum")
   
                    HStack 
                        Spacer()
                        if BeanRoaster != "" && BeanName != "" 
                            Button(action: 
                                //....
                                let pickedImage = self.inputImage?.jpegData(compressionQuality: 1.0)
                                                                print("image, inputimage, pickedImage")
                                print(self.image as Any) // prints:  Optional(SwiftUI.Image(provider: SwiftUI.ImageProviderBox<__C.UIImage>))
                                print(self.inputImage as Any) // prints: nil
                                print(pickedImage as Any) // prints: nil
                                //.....
                                let bean = Bean(context: self.moc)
                                bean.id = UUID()
                                bean.roaster = "\(self.BeanRoaster)"
                                bean.name = "\(self.BeanName)"
                                bean.roastStyle = "\(self.BeanStyle)"
                                bean.roastDate = self.RoastDate
                                bean.active = true
                                bean.img = pickedImage
                                
                                try? self.moc.save()
                                self.presentationMode.wrappedValue.dismiss()
                            ) 
                                Image(systemName: "tray")
                                    .foregroundColor(.blue)
                                    .font(.largeTitle)
                                    .padding(.vertical)
                            
                            Text("Save").foregroundColor(.blue)
                            Spacer()
                         else 
                            HStack 
                                Spacer()
                                Text("Trage Bohnendaten ein!")
                                Spacer()
                            
                         
                     

                    Section 
                        HStack 
                            Spacer()
                            Button(action: self.presentationMode.wrappedValue.dismiss()) 
                                Image(systemName: "")
                                    .foregroundColor(.red)
                                    .font(.largeTitle)
                                    .padding(.vertical)
                            
                            Text("Dismiss").foregroundColor(.red)
                            Spacer()
                        
                    
                .navigationBarTitle("New Bean")
            
        
    

【问题讨论】:

SwiftUI Image is-a View,你从图像选择器中获得UIImage,将其保留为模型并在两个地方使用:并在Image中显示并存储在数据库中。 您好 Asperi,感谢您的回答。我正在使用的 ImagePicker 将图像作为图像处理。我不太确定如何处理它。我希望直接显示所选图像。你能给我更多的信息吗?谢谢。 好的,谢谢,我用了另一个ImagePicker:hackingwithswift.com/forums/swiftui/… 可能的简单方法 -> ***.com/questions/65614931/… 【参考方案1】:

上面的代码有一些错误。我重新创建了解决方案,其中还包含更改 ImagePicker。

您的 ImagePicker 所做的是更改 Image 的 @State。您显示该图像,然后将显示您选择的图像。这里一切都很好。但是,您将无法获取图像的 UIImage。看Image类的接口是不可能的。

让你的图像选择器返回一个 UIImage

我不确定您使用的是哪个 ImagePicker。但是,您可以简单地修改版本here 以返回UIImage

只有在显示 imagePicker 和 UIImage 时才存储布尔值。

@State private var showImagePicker : Bool = false
@State private var image : UIImage?

每当 UIImage 发生变化时,您都可以更新您的 Image。只需使用:

if (self.image != nil)

    Image(uiImage: self.image!).resizable().scaledToFit().aspectRatio(contentMode: .fit)

现在,当您将 UIImage 存储到数据库时,您需要将其转换为 Data 对象。这很简单。

Button(action: 
    
    //Not nil
    print(self.image)

    //PNG Representation as Data
    let imageData = self.image!.pngData()
    
    //Data type here --> Data
    print(type(of: imageData))

你看,它来自对象Data,数据可以简单地存储在CoreData中。

您仍然需要解决的唯一问题是您没有将图像绑定传递给您的 ImagePicker,而是将绑定作为 UIImage 传递。

看起来像这样:

struct CaptureImageView 
    
    @Binding var isShown: Bool
    @Binding var image: UIImage?
    
    func makeCoordinator() -> Coordinator 
      return Coordinator(isShown: $isShown, image: $image)
    

而在didFinishPickingMediaWithInfo 中,您只需设置 UIImage。

func imagePickerController(_ picker: UIImagePickerController,
            didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) 
 guard let unwrapImage = info[UIImagePickerController.InfoKey.originalImage] as? UIImage else  return 
 imageInCoordinator = unwrapImage
 isCoordinatorShown = false

再说一次,这不是我的 ImagePicker。我只是为您修改了它,以明确什么是错误的以及您需要修复什么。我在这里得到了一个可行的解决方案,这是可能的。只需确保您使用的是什么,而不是 Image,而是 UIImage 以保持对它的引用。

【讨论】:

【参考方案2】:

下面是我现有应用程序中的几行代码,我已对其进行了简化。我的方法是简单地从 Image Picker 取回图像数据,而不是 UIImage。这对我有用。

ContentView

struct ContentView: View 
    @Environment(\.managedObjectContext) var viewContext
    
    @FetchRequest(entity: Images.entity(), sortDescriptors: [], animation: Animation.easeInOut)
    var images: FetchedResults<Images>
    
    @State private var showImagePicker = false
    @State private var image = Image(systemName: "person.circle.fill")
    @State private var data: Data?
    @State private var optionalData = UIImage(systemName: "person.circle.fill")?.jpegData(compressionQuality: 1)
    
    var body: some View 
        VStack 
            ForEach(images)  image in
                Image(uiImage: UIImage(data: (image.imageData ?? optionalData)!)!)
                    // your modifiers
            
            
            image
                .resizable()
                .frame(width: 150, height: 150)
            
            Button(action: 
                showImagePicker = true
            ) 
                Text("Select")
            
            
            Button(action: 
                let newImage = Images(context: viewContext)
                newImage.imageData = data

                do 
                    try viewContext.save()
                    print("Image Saved!")
                 catch 
                    print(error.localizedDescription)
                
            ) 
                Text("Save")
            
        
        .sheet(isPresented: $showImagePicker, onDismiss: loadImage) 
            ImagePicker(data: $data)
        
    
    
    func loadImage() 
        guard let data = data else  return 
        
        image = Image(uiImage: UIImage(data: data) ?? UIImage(systemName: "person.circle.fill")!)
    

ImagePicker

struct ImagePicker: UIViewControllerRepresentable 
    @Environment(\.presentationMode) var presentationMode
    @Binding var data: Data?
    
    class Coordinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate 
        var parent: ImagePicker
        
        init(_ parent: ImagePicker) 
            self.parent = parent
        
        
        func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) 
            if let uiImage = info[.originalImage] as? UIImage 
                parent.data = uiImage.jpegData(compressionQuality: 1)
            
            parent.presentationMode.wrappedValue.dismiss()
        
    
    
    func makeUIViewController(context: Context) -> UIImagePickerController 
        let picker = UIImagePickerController()
        picker.allowsEditing = true
        picker.sourceType = .photoLibrary
        picker.delegate = context.coordinator
        
        return picker
    
    
    func makeCoordinator() -> Coordinator  Coordinator(self) 
    
    func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context)   

【讨论】:

以上是关于SwiftUI ImagePicker 保存(图像-> UIImage--> 数据)到核心数据的主要内容,如果未能解决你的问题,请参考以下文章

如何正确地将 ImagePicker 插入标题中的图像? SwiftUI

如何将您使用 imagepicker 上传的照片保存为新帖子?

SwiftUI 将图像保存到 macOS Catalyst

ImagePicker 选择图像后,CollectionView 图像不更新

无法使用模拟器从 iOS 中的 ImagePicker 获取图像路径和图像?

如何在 ImagePicker 控制器的单元格类中设置图像