I'm new to iOS dev and cannot figure out how to load an image in an imageView from a local directory.
I use imagePickerController to pick an image, get its info, then use this information to display the image in the imageView.
Here is the code to pick the image and its info dictionary:
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!)!
imagesList.append(localPath)
picker.dismiss(animated: true, completion: nil)
}
Then, and here is where I'm looking for some help, I want to use either localPath or imageUrl to load the image in another imageView but cannot figure out how.
I tried
func changeImage(_ sender: Any) {
if imagesList.count > 0 {
let imageUrlPath = imagesList[indexList]
let urlString: String = imageUrlPath.absoluteString
imageView.image = UIImage(contentsOfFile: urlString)
indexList = (indexList + 1) % imagesList.count
}
}
But I cannot have something working.
Any idea how I can achieve that?
Thank you for your help.
You'll want to save a reference to PHAsset objects, not URL strings.
Start with defining your Array as:
var imagesList = [PHAsset]()
Then, in didFinishPickingMediaWithInfo:
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
dismiss(animated: true)
// get the selected image as a UIImage object
let image = info[UIImagePickerControllerOriginalImage] as! UIImage
imageView.image = image
// save a PHAsset reference in imagesList array for later use
if let imageUrl = info[UIImagePickerControllerReferenceURL] as? URL{
let assets = PHAsset.fetchAssets(withALAssetURLs: [imageUrl], options: nil)
if let p = assets.firstObject {
imagesList.append(p)
}
}
}
Next, add a couple "convenience" functions:
func getAssetFullsize(asset: PHAsset) -> UIImage {
let manager = PHImageManager.default()
let option = PHImageRequestOptions()
var img = UIImage()
option.isSynchronous = true
let w = asset.pixelWidth
let h = asset.pixelHeight
manager.requestImage(for: asset, targetSize: CGSize(width: w, height: h), contentMode: .aspectFit, options: option, resultHandler: {(result, info)->Void in
img = result!
})
return img
}
func getAssetThumbnail(asset: PHAsset) -> UIImage {
let manager = PHImageManager.default()
let option = PHImageRequestOptions()
var thumbnail = UIImage()
option.isSynchronous = true
let w = 100
let h = 100
manager.requestImage(for: asset, targetSize: CGSize(width: w, height: h), contentMode: .aspectFit, options: option, resultHandler: {(result, info)->Void in
thumbnail = result!
})
return thumbnail
}
and finally, when you want to get the actual image:
func changeImage(_ sender: Any) {
if imagesList.count > 0 {
// get the full-size image from PHAsset
imageView.image = getAssetFullsize(asset: imagesList[indexList])
// or, just get a thumbnail-sized image
//imageView.image = getAssetThumbnail(asset: imagesList[indexList])
indexList = (indexList + 1) % imagesList.count
}
}
Naturally, you'll want to add appropriate error-checking, your own sizing, naming, tracking, etc... but I think this is the direction you want to head.
Related
In one of my project there is a functionality to pick image from Photos. So for that I've used one third party lib named "YangMingShan". The functionality works perfect as per my requirement.
But the problem is that, I want to get the image name picked from the Photos. Whenever I picked single image from photos, it called below method. And it gives me image.
func photoPickerViewController(_ picker: YMSPhotoPickerViewController!, didFinishPicking image: UIImage!) {
}
Can anyone please help me to get the image name from image ?
func photoPickerViewController(_ picker: YMSPhotoPickerViewController!, didFinishPickingImages photoAssets: [PHAsset]!) {
// Remember images you get here is PHAsset array, you need to implement PHImageManager to get UIImage data by yourself
picker.dismiss(animated: true) {
let options = PHImageRequestOptions.init()
options.deliveryMode = .highQualityFormat
options.resizeMode = .exact
options.isSynchronous = true
var imagesWithName:[ImageModel] = []
for asset: PHAsset in photoAssets {
print(asset.originalFilename)
let image = asset.getImage()
let fileName = asset.originalFilename
imagesWithName.append(ImageModel(name: fileName, image: image))
}
print(imagesWithName)
// Assign to Array with images
}
}
the extension of PHAsset to get file name as follows
extension PHAsset {
var primaryResource: PHAssetResource? {
let types: Set<PHAssetResourceType>
switch mediaType {
case .video:
types = [.video, .fullSizeVideo]
case .image:
types = [.photo, .fullSizePhoto]
case .audio:
types = [.audio]
case .unknown:
types = []
#unknown default:
types = []
}
let resources = PHAssetResource.assetResources(for: self)
let resource = resources.first { types.contains($0.type)}
return resource ?? resources.first
}
var originalFilename: String {
guard let result = primaryResource else {
return "file"
}
return result.originalFilename
}
func getImage() -> UIImage {
let manager = PHImageManager.default()
let option = PHImageRequestOptions()
var thumbnail = UIImage()
option.isSynchronous = true
manager.requestImage(for: self,
targetSize: CGSize(width: self.pixelWidth, height: self.pixelHeight),
contentMode: .aspectFit,
options: option,
resultHandler: {(result, info) -> Void in
thumbnail = result!
})
return thumbnail
}
}
and the model
struct ImageModel {
let name:String
let image:UIImage
}
replace the line 77 in Pod -> YangMingSha -> YMSPhotoPicker -> YMSPhotoPickerViewController.h with
- (void)photoPickerViewController:(YMSPhotoPickerViewController *)picker didFinishPickingImage:(UIImage *)image didFinishPickingAssets:(PHAsset *)photoAssets;
then after build it will raise two error for that photoAsset parameter missing in delete function
in YMSPhotoPickerViewController.m replace the error part line 256 with
[self.delegate photoPickerViewController:self
didFinishPickingImage:[self yms_orientationNormalizedImage:image] didFinishPickingAssets:asset];
and in other error also replace the code with
PHAsset *asset = self.currentCollectionItem[#"assets"];
[self.delegate photoPickerViewController:self
didFinishPickingImage:[self yms_orientationNormalizedImage:image] didFinishPickingAssets:asset];
and the singleImage delegate method now should be like this
func photoPickerViewController(_ picker: YMSPhotoPickerViewController!, didFinishPicking image: UIImage!, didFinishPickingAssets photoAssets: PHAsset!) {
let asset = photoAssets.originalFilename
let image = photoAssets.getImage()
print(asset)
picker.dismiss(animated: true)
}
I’m try to get the size in bytes of UIImage. The problem is that my actual image is of 5MB and when i get the size using
let image = info[UIImagePickerController.InfoKey.originalImage] as! UIImage
let imgData = NSData(data: image.jpegData(compressionQuality: 1)!)
var imageSize: Int = imgData.count
print("actual size of image in KB: %f ", Double(imageSize) / 1000.0)
I’m getting 2MB only. can anyone enlighten me on this please.
Doesn't this line recompress the image?
let imgData = NSData(data: image.jpegData(compressionQuality: 1)!)
If so, I'd definitely expect the resulting image to be smaller than the original.
This worked for me instead of UIImage().jpegdata
public func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
var asset: PHAsset!
if #available(iOS 11.0, *) {
asset = info[UIImagePickerControllerPHAsset] as? PHAsset
} else {
if let url = info[UIImagePickerControllerReferenceURL] as? URL {
asset = PHAsset.fetchAssets(withALAssetURLs: [url], options: .none).firstObject!
}
}
if #available(iOS 13, *) {
PHImageManager.default().requestImageDataAndOrientation(for: asset, options: .none) { data, string, orien, info in
let imgData = NSData(data:data!)
var imageSize: Int = imgData.count
print("actual size of image in KB: %f ", Double(imageSize) / 1024.0)
}
} else {
PHImageManager.default().requestImageData(for: asset, options: .none) { data, string, orientation, info in
let imgData = NSData(data:data!)
var imageSize: Int = imgData.count
print("actual size of image in KB: %f ", Double(imageSize) / 1024.0)
}
}
}
Add option .original to the PHImageRequestOptions. This one should do the trick:
let options = PHImageRequestOptions()
options.deliveryMode = .highQualityFormat
options.resizeMode = .none
options.version = .original
PHImageManager.default().requestImageDataAndOrientation(for: asset, options: options)
Am creating an application for image share related things. Here my requirement is I have to store some custom information(Name, PhoneNumber, Price) into the Image Metadata and retrieve it back.
I use UIImagePickerController to capture the image and set my information into the image metadata in UIImagePickerControllerDelegate like below mentioned:
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
picker.dismiss(animated: true, completion: nil)
let profileImage = info[UIImagePickerControllerOriginalImage] as? UIImage
let imageData: Data = UIImageJPEGRepresentation(profileImage!, 1)!
let cgImgSource: CGImageSource = CGImageSourceCreateWithData(imageData as CFData, nil)!
let uti: CFString = CGImageSourceGetType(cgImgSource)!
let dataWithExif: NSMutableData = NSMutableData(data: imageData)
let destination: CGImageDestination = CGImageDestinationCreateWithData((dataWithExif as CFMutableData), uti, 1, nil)!
let imageProoperties = CGImageSourceCopyPropertiesAtIndex(cgImgSource, 0, nil)! as NSDictionary
let mutable: NSMutableDictionary = imageProoperties.mutableCopy() as! NSMutableDictionary
let EXIFDictionary: NSMutableDictionary = (mutable[kCGImagePropertyExifDictionary as String] as? NSMutableDictionary)!
print("Before Modification: \(EXIFDictionary)")
EXIFDictionary[kCGImagePropertyExifUserComment as String] = "\(self.m_NameTxtFd.text!):\(self.m_PhoneNumberTxtFd.text!):\(self.m_PriceTxtFd.text!)"
mutable[kCGImagePropertyExifDictionary as String] = EXIFDictionary
CGImageDestinationAddImageFromSource(destination, cgImgSource, 0, (mutable as CFDictionary))
CGImageDestinationFinalize(destination)
let testImage: CIImage = CIImage(data: dataWithExif as Data, options: nil)!
let newProperties: NSDictionary = testImage.properties as NSDictionary
print("After Modification : \(newProperties)") //Here I Got My Information is Stored Successfully
self.m_ImgView.image = self.convert(cmage: testImage)
self.saveImageDocumentDirectory()
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
picker.dismiss(animated: true, completion: nil)
}
Now am going to save the image in NSDocumentDirectory like below mentioned:
func saveImageDocumentDirectory(){
let fileManager = FileManager.default
let paths = (NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString).appendingPathComponent("apple.jpg")
let image = self.m_ImgView.image
print(paths)
let imageData = UIImageJPEGRepresentation(image!, 0.5)
fileManager.createFile(atPath: paths as String, contents: imageData, attributes: nil)
}
Now am going to fetch the stored image in another view controller like below mentioned:
func getImage(){
let fileManager = FileManager.default
let imagePAth = (self.getDirectoryPath() as NSString).appendingPathComponent("apple.jpg")
print(imagePAth)
if fileManager.fileExists(atPath: imagePAth){
self.m_ImgView.image = UIImage(contentsOfFile: imagePAth)
self.fetchImageDetails()
}else{
print("No Image")
}
}
I successfully got the image and now I have to fetch the information from image metadata like below mentioned:
func fetchImageDetails() {
let profileImage = self.m_ImgView.image!
let ciImage: CIImage = CIImage(cgImage: profileImage.cgImage!)
let newProperties: NSDictionary = ciImage.properties as NSDictionary
}
But issue is the information is null in image property.
Please guide me to retrieve the custom information from stored Image.
First create NSMutableDictionary and set value to NSMutableDictionary when you set value to then you don't need to set again to metadata you directly assign to NSMutableDictionary to Metada.
let metadata = info[UIImagePickerControllerMediaMetadata] as? NSMutableDictionary
let exifData = NSMutableDictionary()
let metaStr = "\(self.m_NameTxtFd.text!),\(self.m_PhoneNumberTxtFd.text!),\(self.m_PriceTxtFd.text!)"
exifData.setValue(metaStr, forKey: kCGImagePropertyExifDictionary as String)
metadata = exifData
fileManager.requestImageData(for: fetchResult.object(at: i) as PHAsset, options: requestOptions, resultHandler: { (imagedata, dataUTI, orientation, info ) in
if let info = info {
if info.keys.contains(NSString(string: "PHImageFileURLKey")) {
path = info[NSString(string: "PHImageFileURLKey")] as? NSURL
size = (imagedata! as NSData).length
self.name = PHAssetResource.assetResources(for:fetchResult.object(at: i)).first?.originalFilename
self.imageData = imagedata
}
}
})
I am able select image from gallery and save to UIImageView but when trying of camera i can not able save the image in UIImageView. it calls didFinishPickingMediaWithInfo method but its going inside UIImagePickerControllerOriginalImage if case.
here is the code i used for camera and gallery picker:
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
if let pickedImage = info[UIImagePickerControllerImageURL] as? URL {
if let data : Data = try? Data(contentsOf: pickedImage as URL)
{
print(data.format)
if data.format == "gif" {
let data = try? Data(contentsOf: pickedImage) //make sure your image in this url does exist, otherwise unwrap in a if let check / try-catch
let imageView = UIImage(data: data!)
let width = imageView?.size.width
let height = imageView?.size.height
let url = SaveItemFilemanager.sharedinstance.saveGifimage(data: data!)
addGifImage(url: url!, width: width!, height: height!)
} else {
if let photo = info[UIImagePickerControllerLivePhoto] as? PHLivePhoto {
let assetResources = PHAssetResource.assetResources(for: photo)
let asset = assetResources[1]
let url = SaveItemFilemanager.sharedinstance.saveLiveimage(data: asset)
print(url!)
let frameCount = 16
let delayTime = Float(0.2)
let loopCount = 0
let width = photo.size.width
let height = photo.size.height
let regift : Regift = Regift(sourceFileURL: url!, frameCount: frameCount, delayTime: delayTime, loopCount: loopCount)
addGifImage(url: regift.createGif()!, width: width, height: height)
} else if let normalImage = info[UIImagePickerControllerOriginalImage] as? UIImage {
let width = normalImage.size.width
let height = normalImage.size.height
let url = SaveItemFilemanager.sharedinstance.saveimage(image: normalImage)
addImage(url: url!, width: width, height: height)
} else {
let normalImage = info[UIImagePickerControllerOriginalImage] as? UIImage
let width = normalImage?.size.width
let height = normalImage?.size.height
let url = SaveItemFilemanager.sharedinstance.saveimage(image: normalImage!)
addImage(url: url!, width: width!, height: height!)
}
}
}
}
dismiss(animated: true, completion: nil)
}
func showCamera() {
let imagePicker = UIImagePickerController()
if(UIImagePickerController .isSourceTypeAvailable(.camera))
{
imagePicker.delegate = self
imagePicker.sourceType = .camera
}
present(imagePicker, animated: true, completion: nil)
}
I checked even by keeping break point in didfinishpicking media it calls and dismissed immediately.
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.