Search code examples
swiftuiimageuiimagepickercontroller

How to convert 'UIImagePickerController.InfoKey.originalImage' into 'UIImage'


I'm developping an iOS phone app and I want to take a picture and store it in the app.

I am able to take the picture using a UIImagePickerController but I can't store it in my database. I'm using CoreData to store data in this app but for the picture, it seems that it is easier to store the picture in a folder and store the file path in Coredata. The issue is that I can get the picture i've took with the camera but I can't get the PNG data to store it in my folder.

func takePhoto(){
        //Take the picture with the camera
        imagePickerController = UIImagePickerController()
        imagePickerController.delegate = self
        if !UIImagePickerController.isSourceTypeAvailable(.camera){
            let alertController = UIAlertController.init(title: nil, message: "Camera is not available", preferredStyle: .alert)
            let okAction = UIAlertAction.init(title: "Alright", style: .default, handler: {(alert: UIAlertAction!) in })
            alertController.addAction(okAction)
            self.present(alertController, animated: true, completion: nil)
        }
        else{ imagePickerController.sourceType = .camera }

        present(imagePickerController, animated: true, completion: nil)

        //Store the picture in an app folder
        let fileManager = FileManager.default
        let imagePath = (NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString).appendingPathComponent("image")
        let picture = UIImagePickerController.InfoKey.originalImage as? UIImage
        let data = picture?.pngData()
        fileManager.createFile(atPath: imagePath as String, contents: data, attributes: nil)

        //Store the file path in CoreData
        let image = Image(context: self.persistenceManager.context)
        image.pathName = imagePath
        self.persistenceManager.saveContext()
}

The problem is when I try to get the png data. I need a UIImage to use .pngData() but when I try to convert my picture object into UIImage from UIImagePickerController.InfoKey.originalKey I have the following warning message:

"Cast from 'UIImagePickerController.InfoKey' to unrelated type 'UIImage' always fails". Also, I don't understand why I need to convert it because I found in the AppleDevelopper website that: "The value for this key is a UIImage object." (the key is "static let originalImage: UIImagePickerController.InfoKey" )

I've also tried to let my picture object as an UIImagePickerController.InfoKey object without converting it into an UIImage object but in this case I have the following error message:

"Value of type 'UIImagePickerController.InfoKey' has no member 'pngData' " and the method UIImagePNGRepresentation() is not working because the expected argument type is also 'UIImage'


Solution

  • UIImagePickerController.InfoKey.originalImage is a String... it's a key, not the actual image.

    You should use the UIImagePickerControllerDelegate, like so

    extension ViewController: UIImagePickerControllerDelegate {
    
        func imagePickerController(_ picker: UIImagePickerController, 
          didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
    
            let image = info[.originalImage] as? UIImage
        }
    
    }
    

    You see, the information from the UIPickerController is passed as a dictionary with the type [UIImagePickerController.InfoKey : Any], UIImagePicker.InfoKey contains all of the Keys that are used in this dictionary, so you use these to access the values.

    EDIT:

    So your takePhoto function only needs to display the picker, then handle the response later in the delegate method:

    func takePhoto(){
        //Take the picture with the camera
        imagePickerController = UIImagePickerController()
        imagePickerController.delegate = self
        if !UIImagePickerController.isSourceTypeAvailable(.camera){
            let alertController = UIAlertController.init(title: nil, message: "Camera is not available", preferredStyle: .alert)
            let okAction = UIAlertAction.init(title: "Alright", style: .default, handler: {(alert: UIAlertAction!) in })
            alertController.addAction(okAction)
            self.present(alertController, animated: true, completion: nil)
        }
        else{ imagePickerController.sourceType = .camera }
    
        present(imagePickerController, animated: true, completion: nil)
    }
    

    Then in your delegate you can work with the selected image

    extension ViewController: UIImagePickerControllerDelegate {
    
        func imagePickerController(_ picker: UIImagePickerController, 
          didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
    
           let fileManager = FileManager.default
           let imagePath = (NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString).appendingPathComponent("image")
           let picture = info[.originalImage] as? UIImage
           let data = picture?.pngData()
           fileManager.createFile(atPath: imagePath as String, contents: data, attributes: nil)
    
           //Store the file path in CoreData
           let image = Image(context: self.persistenceManager.context)
           image.pathName = imagePath
           self.persistenceManager.saveContext()
        }
    
    }