In my app, I have a button with which I open the Gallery as below:
#IBAction func selectPhoto() {
let photoPicker = UIImagePickerController()
photoPicker.delegate = self
photoPicker.sourceType = .PhotoLibrary
self.presentViewController(photoPicker, animated: true, completion: nil)
}
I let the user select a photo and save it:
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
photoImageView.image = info[UIImagePickerControllerOriginalImage] as? UIImage
let fileName:String = "logo.png"
let arrayPaths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as NSString
let pngFileName = arrayPaths.stringByAppendingPathComponent(fileName) UIImagePNGRepresentation(photoImageView.image!)!.writeToFile(pngFileName, atomically:true)
imageSave.setObject(fileName, forKey: "pngFileName")
self.dismissViewControllerAnimated(false, completion: nil)
}
Then from View controller, the pictures saved is shown everytime the user gets to the scene using below code.
override func viewDidLoad() {
super.viewDidLoad()
if imageSave.stringForKey("pngFileName") != nil
{
let path = NSSearchPathForDirectoriesInDomains(
.DocumentDirectory, .UserDomainMask, true)[0] as NSString
let fileName = NSUserDefaults.standardUserDefaults()
.stringForKey("pngFileName")
let imagePath = path.stringByAppendingPathComponent(fileName!)
let image = UIImage(contentsOfFile: imagePath )
photoImageView.image = image
}
Problem : When the image loads back onto the UIImageView, the orientation of the picture is sometimes rotated right by 90 degrees, sometimes rotated left by 90 degrees or sometimes shown as it was saved.
Please let me know what I am doing wrong here. Any help would be appreciated. Thanks
I had the same problem, because of different rect sizes where the photo is shown. You could do a trick and save instead of the original image a copied version. This will discard the rotation problem, which is actually a fitting issue.
let imageName = info[UIImagePickerControllerOriginalImage] as! UIImage
let thumb1 = UIImageJPEGRepresentation(imageName, 0.8)
btnImageView.image = UIImage(data: thumb1!)
This is just a quick and dirty answer, but worked for me. :-)
Related
I am using UIImagePickerController to use my camera like so:
#objc func toggle() {
if UIImagePickerController.isSourceTypeAvailable(.camera) {
//Define UIImagePickerController variable
let imagePicker = UIImagePickerController()
//Assign the delegate
imagePicker.delegate = self
//Set image picker source type
imagePicker.sourceType = .camera
//Allow Photo Editing
imagePicker.allowsEditing = true
//Present camera
UIApplication.topViewController()?.present(imagePicker, animated: true, completion: nil)
}
}
Now I am trying to capture the image taken using the didFinishPickingMediaWithInfo method, I got this example online:
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
let imageUrl = info[UIImagePickerControllerOriginalImage] as! NSURL
let imageName = imageUrl.lastPathComponent
let documentDirectory = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first!
let photoURL = NSURL(fileURLWithPath: documentDirectory)
let localPath = photoURL.appendingPathComponent(imageName!)
let image = info[UIImagePickerControllerOriginalImage]as! UIImage
let data = UIImagePNGRepresentation(image)
do
{
try data?.write(to: localPath!, options: Data.WritingOptions.atomic)
}
catch
{
// Catch exception here and act accordingly
}
UIApplication.topViewController()?.dismiss(animated: true, completion: nil);
}
But I changed UIImagePickerControllerReferenceURL to UIImagePickerControllerOriginalImage as UIImagePickerControllerReferenceURL is nil. but after I change that I get this fatal error:
Could not cast value of type 'UIImage' (0x1b6b02b58) to 'NSURL'
How do I save the image take from the camera? What am I doing wrong?
Write your code as following this will give you image.
let image = info[UIImagePickerControllerOriginalImage] as! UIImage
UIImagePickerControllerOriginalImage return image not NSURL
Write following code to get image url in iOS 11. From iOS 11 UIImagePickerControllerImageURL is available, earlier there are UIImagePickerControllerMediaURL key to get image url.
if #available(iOS 11.0, *) {
if let imageURL = info[UIImagePickerControllerImageURL] as? URL {
print(imageURL)
}
} else {
if let imageUrl = info[UIImagePickerControllerMediaURL] as? URL {
print(imageUrl)
}
}
I hope this will help you.
The one who are searching for complete method to implement for Swift 4.2+
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let pickedImage = info[UIImagePickerController.InfoKey.originalImage] as? UIImage{
imageView.image = pickedImage
}
imgPicker.dismiss(animated: true, completion: nil)
}
This will return you the original image according to new syntax
For Image URL and Media URL, Use the respective
let imgURL = info[UIImagePickerController.InfoKey.imageURL]
let mediaURL = info[UIImagePickerController.InfoKey.mediaURL]
I'm working on image picker with camera and gallery and using following code to get image captured from camera but imageurl and image name is nil.I'm testing on mobile with os version of 11.2.2.
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
let image = info[UIImagePickerControllerOriginalImage] as! UIImage
let imageUrl = info[UIImagePickerControllerReferenceURL] as? NSURL
let imageName = imageUrl?.lastPathComponent
let documentDirectory = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first!
let photoURL = NSURL(fileURLWithPath: documentDirectory)
let localPath = photoURL.appendingPathComponent(imageName!)
if !FileManager.default.fileExists(atPath: localPath!.path) {
do {
try UIImageJPEGRepresentation(image, 1.0)?.write(to: localPath!)
print("file saved")
}catch {
print("error saving file")
}
}
else {
print("file already exists")
}
}
Use UIImagePickerControllerImageURL instead of UIImagePickerControllerReferenceURL like this:
let imageUrl = info[UIImagePickerControllerImageURL] as? NSURL
You will not get any imageUrl or imageName because you are using the camera mode and that image is still not saved in your gallery and hence no image path is created for that camera clicked pic. So they will be nil. You can only get the UIImage object info[UIImagePickerControllerOriginalImage] in this case.
If you pick the image from photo album, then you will get everyhting. You can get the url by info[UIImagePickerControllerImageURL] and you can grab the name using the last path component.
Change your imagePickerController source to photo imagePickerController.sourceType = .photoLibrary and then you will get the urls.
This below code is working perfectly fine for images picked from gallery. But will not work if taken with Camera. I tried to save image into storage and read again, but I was unable to do that. So could any one help me in this? Thank you.
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
if let referenceUrl = info[UIImagePickerControllerReferenceURL] as? NSURL, image = info[UIImagePickerControllerOriginalImage] as? UIImage {
let phAsset = PHAsset.fetchAssetsWithALAssetURLs([referenceUrl], options: nil).lastObject as! PHAsset
PHImageManager.defaultManager().requestImageDataForAsset(phAsset, options: PHImageRequestOptions(), resultHandler: { (imagedata, dataUTI, orientation, info) in
if info!.keys.contains(NSString(string: "PHImageFileURLKey")) {
let path = info![NSString(string: "PHImageFileURLKey")] as! NSURL
print("path q\(path)")
self.mImageUrl = path
self.mlocalPath = path.path
self.mImageExtension = path.pathExtension
self.mImageName = path.lastPathComponent!
print("mImageName q\(self.mImageName)")
}
})
}
dismissViewControllerAnimated(true, completion: nil)
}
Swift 5+
As the previous answers sugested, the image is not stored in gallery yet and hence no imageName. You need to store it in gallery. Use the below Helper class to save and get images from FileManager.
Thanks to this Answer
class CameraImageManager {
static let shared = CameraImageManager()
public func saveImage(imageName: String, image: UIImage) {
guard let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return }
let fileName = imageName
let fileURL = documentsDirectory.appendingPathComponent(fileName)
guard let data = image.jpegData(compressionQuality: 1) else { return }
//Checks if file exists, removes it if so.
if FileManager.default.fileExists(atPath: fileURL.path) {
do {
try FileManager.default.removeItem(atPath: fileURL.path)
print("Removed old image")
} catch let removeError {
print("couldn't remove file at path", removeError)
}
}
do {
try data.write(to: fileURL)
} catch let error {
print("error saving file with error", error)
}
}
public func getImagePathFromDiskWith(fileName: String) -> URL? {
let documentDirectory = FileManager.SearchPathDirectory.documentDirectory
let userDomainMask = FileManager.SearchPathDomainMask.userDomainMask
let paths = NSSearchPathForDirectoriesInDomains(documentDirectory, userDomainMask, true)
if let dirPath = paths.first {
let imageUrl = URL(fileURLWithPath: dirPath).appendingPathComponent(fileName)
return imageUrl
}
return nil
}
public func loadImageFromDiskWith(fileName: String) -> UIImage? {
let documentDirectory = FileManager.SearchPathDirectory.documentDirectory
let userDomainMask = FileManager.SearchPathDomainMask.userDomainMask
let paths = NSSearchPathForDirectoriesInDomains(documentDirectory, userDomainMask, true)
if let dirPath = paths.first {
let imageUrl = URL(fileURLWithPath: dirPath).appendingPathComponent(fileName)
let image = UIImage(contentsOfFile: imageUrl.path)
return image
}
return nil
}
}
Now, in your imagePickerController didFinishPickingMediaWithInfo callback function, this is how you can assign a name to an image and save it.
public func imagePickerController(_ picker: UIImagePickerController,
didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
guard let image = info[.editedImage] as? UIImage else { return }
let imageName = "RDV_" + UUID().uuidString
CameraImageManager.shared.saveImage(imageName: imageName, image: image)
print("IMAGE NAME IS: ", imageName)
}
Hope It Helps.
You can use a notification with addObserver like this
ViewController A : where you want image to be changed, add this in viewDidLoad
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(self.methodOfImageChange(_:)), name:"ImageChanged", object: nil)
Add this method in ViewController A
func methodOfImageChange(notification: NSNotification){
let appStatus = notification.userInfo as? Dictionary<String,AnyObject>
// appStatus contains your image in "image" key
}
Now in didFinishPickingMediaWithInfo add this
let dictionary: [String:AnyObject] = [
"image" : (info[UIImagePickerControllerOriginalImage] as? UIImage)!,
]
NSNotificationCenter.defaultCenter().postNotificationName("ImageChanged", object: self, userInfo: dictionary)
picker .dismissViewControllerAnimated(true, completion: nil)
Hope this helps
The image isn't in the gallery yet, so I don't believe you have a name.
In my app the flow (via navigation controller) is:
Selection VC (choice of Camera or Photo Library) ->
UIImagePickerController ->
Edit VC (with back navigation and action button for - among others - saving to Photo Library)
If the user chooses Camera, they take a picture and the options are "Retake" or "Use Photo". Is they choose "Use Photo", they are in the Edit VC.
If they then choose to go back to the Select VC, the image is nowhere to be found.
I know I must be missing something in this code. If it has already been asked I don't know what term to search for. I searched about read picture in document folder blank, or empty. I knew it wasn't nil because Xcode said that it could not be nil for that type (UIImage).
I am able to use imagepicker to get a picture and display it in a UIImageView. When I save the UIImage into the Documents folder there is no error. However, when I read that saved image and try to display it in a UIImageView the picture is blank, or empty (it just shows the background color of the View.)
I am able to copy some pictures from the contents folder in the app into the Documents folder and they display properly. I went through and added print statements throughout the code to see if there was a place code was being skipped. I even wrote a function to list the filenames in the Documents folder. The names of the pictures are there, but the data for the picture that I would save doesn't seem to have been written.
func addNewPhoto() {
gotAPicture = true
if UIImagePickerController.isSourceTypeAvailable(UIImagePickerControllerSourceType.PhotoLibrary) {
let picker = UIImagePickerController()
picker.modalPresentationStyle = .CurrentContext
picker.allowsEditing = false
picker.sourceType = UIImagePickerControllerSourceType.PhotoLibrary
picker.delegate = self
presentViewController(picker, animated: true, completion: nil)
}
}
func imagePickerControllerDidCancel(picker: UIImagePickerController) {
dismissViewControllerAnimated(true, completion: nil)
}
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
var newImage: UIImage
if let possibleImage = info[UIImagePickerControllerEditedImage] as? UIImage {
newImage = possibleImage
} else if let possibleImage = info[UIImagePickerControllerOriginalImage] as? UIImage {
newImage = possibleImage
} else {
return
}
let imageName = NSUUID().UUIDString + ".jpg"
let imagePath = getDocumentsDirectory().stringByAppendingPathComponent(imageName)
if let pictureData = UIImageJPEGRepresentation(newImage, 0.9) {
pictureData.writeToFile(imagePath, atomically: true)
}
ChosenImage.image = newImage
picker.dismissViewControllerAnimated(true, completion: nil)
picker.delegate = nil
}
func getDocumentsDirectory() -> NSString {
let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
let documentsDirectory = paths[0]
return documentsDirectory
}
I have checked the code, rewritten it, and started from scratch. I even followed coding examples from hackingwithswift.com in lesson 10 (as well as other tutorials). That lesson ended up working once I changed the .allowsEditing to false.
OK, here is the code that I use to display the picture
#IBOutlet weak var ChosenImage: UIImageView!
And, here is the code I am using to read the pictures in the Documents folder. It is in a different view controller and the outlet is called mainImage.
let fm = NSFileManager.defaultManager()
let path = getDocumentsDirectory()
let items = try! fm.contentsOfDirectoryAtPath(path as String)
imageList.removeAll()
currentImage = 0
for item in items {
if item.hasSuffix(".jpg") {
imageList.append(item)
}
}
mainImage.image = UIImage(named: imageList[currentImage])
let swipeRight = UISwipeGestureRecognizer(target: self, action: #selector(ViewController.swipeRight(_:)))
swipeRight.direction = .Right
self.view.addGestureRecognizer(swipeRight)
let swipeLeft = UISwipeGestureRecognizer(target: self, action: #selector(ViewController.swipeLeft(_:)))
swipeLeft.direction = .Left
self.view.addGestureRecognizer(swipeLeft)
let swipeUp = UISwipeGestureRecognizer(target: self, action: #selector(ViewController.swipeUp(_:)))
swipeUp.direction = .Up
self.view.addGestureRecognizer(swipeUp)
What is causing the data of the UIImage to not get written along with the filename? I am sure that I missed something.
Thanks.
You should not use this kind of initialisation for image like let image = UIImage(named: <#imageName#>), because this kind of initialisation better use only for images that exists in your application bundle. I suppose that you will suit with method like let image = UIImage(contentsOfFile: <#imagePath#>) for clearance that you load images, that you persist earlier.
However, you problem is simple, when you get your contents of directory, the output will like that
21AB15AE-D69A-416B-AE93-6DC334E41F91.jpg
6CB9FA7B-FF20-4D60-B7EB-B44300C85AF9.jpg
86508AEC-AE8B-4FB8-8CA5-307DF58E959A.jpg
9C90944E-87EE-4B41-BA99-A6A8171402AC.jpg
AC2BC0D4-CA51-4A24-9D80-87F8736A72A7.jpg
B0C772A6-8354-4C7C-B23B-7414123E52FB.jpg
B10FE95C-E017-411C-BAD1-986A6090F463.jpg
DCB4B0C9-DDB4-4188-8DF7-0ABFD2FD76CB.jpg
EEDDCABD-A707-4CAA-9AF7-5009EAA57FA8.jpg and so on...
That leads to main problem - you try to load images just by referring to it only their name, and that produce nil-image as result. To load image you should specify full path to image (full-path will work both for contentsOfURL and named initializers)
I'd create simple project, you can get it here
I'm building an app where I let the user to pick an image from its photo library. I was using this code to save it.
func imagePickerController(picker: UIImagePickerController, didFinishPickingImage image: UIImage, editingInfo: [String : AnyObject]?) {
imgPicker.dismissViewControllerAnimated(true, completion: nil)
NSUserDefaults.standardUserDefaults().setValue(image, forKey: "bgImage")
}
func imagePickerControllerDidCancel(picker: UIImagePickerController) {
imgPicker.dismissViewControllerAnimated(true, completion: nil)
}
I later figured out that you can't save the image with NSUserDefaults, but you can save the picked image's path, save it and load it with "if let..."
But I don't know how to do this (find the path and save it). The idea is for the user to choose the view background image, kinda like how Whatsapp does.
If I'm wrong and you can't store the path, is there any easy way to save the picked image without using online servers?
You can save and retrieve it using NSUserDefaults:
//Save image
let img = UIImage() //Change to be from UIPicker
let data = UIImagePNGRepresentation(img)
NSUserDefaults.standardUserDefaults().setObject(data, forKey: "myImageKey")
NSUserDefaults.standardUserDefaults().synchronize()
//Get image
if let imgData = NSUserDefaults.standardUserDefaults().objectForKey("myImageKey") as? NSData {
let retrievedImg = UIImage(data: imgData)
}
Or you can read / write to file, if you prefer:
//Save image
let img = UIImage() //Change to be from UIPicker
let data = UIImagePNGRepresentation(img)!
do {
let documentsPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as String
try data.writeToFile("\(documentsPath)myImage", options: [])
} catch {
print("Error")
}
//Get image
do {
let documentsPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as String
let readData = try NSData(contentsOfFile: "\(documentsPath)myImage", options: [])
let retreivedImage = UIImage(data: readData)
}
catch {
print("Error")
}
Swift 3:
//Save image
let data = UIImagePNGRepresentation(pickedImage)!
do {
let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as String
try data.write(to: URL(string: "\(documentsPath)/myImage")!, options: .atomic)
} catch {
print("Error")
}
//Get image
do {
let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as String
let readData = try Data(contentsOf: URL(string: "\(documentsPath)/myImage")!)
let retreivedImage = UIImage(data: readData)
} catch {
print("Error")
}
There are two ways, one that you are describing but that is quite messy, the one I would suggest you is to take the image, turn into image data and store it in your application locally like in a sqlite database or in an array in user default. However saving that much data in user default is not a good practise. So, I would go with store it locally and I won't do it with messy sqlite codes, rather I would do it with core data.
class AddImageViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
#IBOutlet var popUpThePickerButton: UIButton! //click on this button to appear the imagePicker
#IBOutlet var addPictureButton: UIButton! //when this button clicked saveImage method is called and there you save the imageData wherever you want
let picker = UIImagePickerController()
var selectedImage : UIImage!
override func viewDidLoad() {
super.viewDidLoad()
picker.delegate = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
// when popUpThePickerButton is clicked
#IBAction func selectPictureFromPhotos(sender: UIButton) {
picker.editing = false
picker.sourceType = .PhotoLibrary
presentViewController(picker, animated: true, completion: nil)
}
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
self.selectedImage = info[UIImagePickerControllerOriginalImage] as! UIImage
dismissViewControllerAnimated(true, completion: nil)
}
func imagePickerControllerDidCancel(picker: UIImagePickerController) {
dismissViewControllerAnimated(true, completion: nil)
}
// then save this image data in your database and when you want to show that image you can just turn the image data to image again
#IBAction func saveImage(sender: UIButton) {
let imageData = UIImagePNGRepresentation(self.selectedImage)
//save this image data in database
}
}