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'
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()
}
}
As #Scriptabel has stated,
you'll need to use this delegate and access/save your image inside the function.
Class VC: UIViewController, UIImagePickerControllerDelegate {
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
let picture = info[UIImagePickerController.InfoKey.originalImage] as! UIImage
let data = picture.pngData()
//Store the picture in an app folder
let fileManager = FileManager.default
let imagePath = (NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString).appendingPathComponent("image")
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()
}
}
Related
I'm trying to figure out how to save a video in my Photo Library. To get started I tried to simply pick a video by using the UIImagePickerController and after picking it saving it again in the library using the UISaveVideoAtPathToSavedPhotosAlbum. Doesn't make much sense but I try to understand how saving videos work.
The following code does not work, as it does not save the video:
#IBAction func ChooseVideo(_ sender: Any) {
let imagePickerController = UIImagePickerController()
imagePickerController.delegate = self
imagePickerController.mediaTypes = ["public.movie"]
self.present(imagePickerController, animated: true, completion: nil)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
let videoURL = info[UIImagePickerController.InfoKey.mediaURL] as? URL
print(UIVideoAtPathIsCompatibleWithSavedPhotosAlbum(videoURL!.path))
dismiss(animated: true, completion: {
UISaveVideoAtPathToSavedPhotosAlbum(videoURL!.path, self, #selector(self.video(_:didFinishSavingWithError:contextInfo:)), nil)
})
}
I hope you can help me as I couldn't find much about saving videos.
Best regards,
MB
Solution:
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any])
{
// *** store the video URL returned by UIImagePickerController *** //
let videoURL = info[UIImagePickerController.InfoKey.mediaURL] as! URL
// *** load video data from URL *** //
let videoData = NSData(contentsOf: videoURL)
// *** Get documents directory path *** //
let paths = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)[0]
// *** Append video file name *** //
print(paths)
let dataPath = paths.appending("/videoFileName.mp4")
// *** Write video file data to path *** //
videoData?.write(toFile: dataPath, atomically: false)
PHPhotoLibrary.shared().performChanges({
PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: URL(fileURLWithPath: dataPath))
}) { saved, error in
if saved {
let fetchOptions = PHFetchOptions()
fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
let fetchResult = PHAsset.fetchAssets(with: .video, options: fetchOptions).firstObject
// fetchResult is your latest video PHAsset
// To fetch latest image replace .video with .image
}
}
}
Use following func to save video to documents directory
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [NSObject : AnyObject])
{
// *** store the video URL returned by UIImagePickerController *** //
let videoURL = info[UIImagePickerControllerMediaURL] as! NSURL
// *** load video data from URL *** //
let videoData = NSData(contentsOfURL: videoURL)
// *** Get documents directory path *** //
let paths = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)[0]
// *** Append video file name *** //
let dataPath = documentsDirectory.stringByAppendingPathComponent("/videoFileName.mp4")
// *** Write video file data to path *** //
videoData?.writeToFile(dataPath, atomically: false)
}
now save this video in photo gellary
PHPhotoLibrary.shared().performChanges({
PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: Your document directory file)
}) { saved, error in
if saved {
let fetchOptions = PHFetchOptions()
fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
let fetchResult = PHAsset.fetchAssets(with: .video, options: fetchOptions).firstObject
// fetchResult is your latest video PHAsset
// To fetch latest image replace .video with .image
}
}
after it if you don't need then delete the image from document directory
, I hope it will work for you ...:)
i have a problem with inserting images into a CollectionView cell using an ImagePicker. The problem is, if I click on a photo which I would like to insert quickly several times, then the photo is inserted several times in different cells.
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
picker.dismiss(animated: true, completion: nil)
picker.allowsEditing = true
if let pickedImage = info[UIImagePickerControllerOriginalImage] as? UIImage {
collectionViewController?.imageArray.append(pickedImage)
collectionViewController?.newImages.append(pickedImage)
let guid = UUID()
let fileManager = FileManager.default
let imagePath = (NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString).appendingPathComponent("\(guid).png")
let data = UIImagePNGRepresentation(fixOrientation(img: pickedImage))
fileManager.createFile(atPath: imagePath as String, contents: data, attributes: nil)
let image = CoreDataManager.shared.createImageyObject(pfad: imagePath, date: Date(), guid: guid)
collectionViewController?.images.append(image)
if collectionViewController?.images.count == 1 {
collectionViewController?.images[0].isBaseImage = true
collectionViewController?.lastIndex = 0
}
collectionViewController?.update()
}
}
Mediainfo contains image url, media url... Maybe you should cache picked imageURL (that unique) in a Set then check if imageURL that's already existed.
How can I make this imagePickerController able to accept edited images?
When selecting an image and adjusting it (zoom in and out) and selecting done, the image reverts back to its original state. What can I add to to my code to have the zoomed image save and show up in my image view?
The following is my code so far:
let cameraRollAction = UIAlertAction(title: "Camera Roll", style: .default) { (action) in
if UIImagePickerController.isSourceTypeAvailable(UIImagePickerControllerSourceType.photoLibrary) {
let picker = UIImagePickerController()
picker.delegate = self
picker.allowsEditing = true
picker.mediaTypes = [kUTTypeImage as String]
picker.sourceType = UIImagePickerControllerSourceType.photoLibrary
self.present(picker, animated: true, completion: nil)
self.newPic = false
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
let mediaType = info[UIImagePickerControllerMediaType] as! NSString
if mediaType.isEqual(to: kUTTypeImage as String) {
let selectedImage = info[UIImagePickerControllerOriginalImage] as! UIImage
profileImageView.image = selectedImage
if newPic == true {
UIImageWriteToSavedPhotosAlbum(selectedImage, self, #selector(imageError), nil)
}
}
self.dismiss(animated: true, completion: nil)
}
Instead of
UIImagePickerControllerOriginalImage
use
UIImagePickerControllerEditedImage
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
let mediaType = info[UIImagePickerControllerMediaType] as! NSString
if mediaType.isEqual(to: kUTTypeImage as String) {
let selectedImage = info[UIImagePickerControllerEditedImage] as! UIImage ?? info[UIImagePickerControllerOriginalImage] as! UIImage // will return original image if edited image is not available
profileImageView.image = selectedImage
if newPic == true {
UIImageWriteToSavedPhotosAlbum(selectedImage, self, #selector(imageError), nil)
}
}
self.dismiss(animated: true, completion: nil)
}
It looks like the didFinishPickingMediaWithInfo function is going on an infinite loop and it eventually crashes with an error that says in the console:
warning: could not execute support code to read Objective-C class data in >the process. This may reduce the quality of type information available.
Right when I record a video and press the choose button, it crashes because it calls the didFinishPickingMediaWithInfo. Here is the relevant code:
let imagePicker: UIImagePickerController! = UIImagePickerController()
let saveFileName = "/test.mp4"
if (UIImagePickerController.isSourceTypeAvailable(.camera)) {
if UIImagePickerController.availableCaptureModes(for: .rear) != nil {
//if the camera is available, and if the rear camera is available, the let the image picker do this
imagePicker.sourceType = .camera
imagePicker.mediaTypes = [kUTTypeMovie as String]
imagePicker.allowsEditing = false
imagePicker.delegate = self as? UIImagePickerControllerDelegate & UINavigationControllerDelegate
imagePicker.videoMaximumDuration = 60
imagePicker.videoQuality = .typeIFrame1280x720
present(imagePicker, animated: true, completion: nil)
} else {
postAlert("Rear camera doesn't exist", message: "Application cannot access the camera.")
}
} else {
postAlert("Camera inaccessable", message: "Application cannot access the camera.")
}
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
print(123)
imagePickerController(imagePicker, didFinishPickingMediaWithInfo: [saveFileName : kUTTypeMovie])
let videoURL = info[UIImagePickerControllerReferenceURL] as? NSURL
print("\(String(describing: videoURL))" )
guard let path = videoURL?.path else { return }
let videoName = path.lastPathComponent
let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
let documentDirectory = paths.first as String!
let localPath = documentDirectory! + "/" + videoName
guard let imageData = NSData(contentsOfFile: localPath) else { return }
let image = UIImage(data: imageData as Data)
picker.dismiss(animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
self.imagePicker.delegate = self
}
Thank you in advance!
You are calling the function from inside of itself, here:
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
print(123)
imagePickerController(imagePicker, didFinishPickingMediaWithInfo: [saveFileName : kUTTypeMovie])
That is causing your infinite loop.
Trying to allow a user to select a photo from their photo library and then display that image in a UIImageView. Photo library is showing up just fine, but when I select a photo from my library, I get this error "[Generic] Creating an image format with an unknown type is an error".
Stepped through my code below but the error comes up only when selecting an image which does not occur inside either of these 2 functions.
#IBAction func openPhotoLibraryButton(sender: UIButton) {
if UIImagePickerController.isSourceTypeAvailable(UIImagePickerControllerSourceType.photoLibrary) {
let imagePicker = UIImagePickerController()
imagePicker.delegate = self
imagePicker.sourceType = UIImagePickerControllerSourceType.photoLibrary;
imagePicker.allowsEditing = false
self.present(imagePicker, animated: true, completion: nil)
}
}
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
let image = info[UIImagePickerControllerOriginalImage] as! UIImage!
let imageData = UIImageJPEGRepresentation(image!, 0.6)
let compressedJPGImage = UIImage(data: imageData!)
imagePicked.image = compressedJPGImage
}
Tried the solutions suggested here: xCode 8 - Creating an image format with an unknown type is an error but none of them worked, and it sounds like several folks did not get this issue resolved. Any ideas?
did you try adding _ before picker? I believe the method was changed and the one you are using is deprecated, you should be able to let it autocomplete.
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
if let image = info[UIImagePickerControllerOriginalImage] as? UIImage {
if let imageData = UIImageJPEGRepresentation(image, 0.6) {
let compressedJPGImage = UIImage(data: imageData)
imagePicked.image = compressedJPGImage
}
} else {
print("Image Picker Failure")
//handle any possible image picker issues/failures so that app doesn't crash in the future
//troubleshoot further if it always hits this
}
dismissViewControllerAnimated(true, completion: nil)
}