I am developing Widgets for iOS and I really don't know how to display local images for the widgets.
func getUIImage(identifiers: [String]) -> UIImage? {
if(identifiers.count <= 0){
return UIImage()
}
let assets: PHFetchResult<PHAsset> = PHAsset.fetchAssets(withLocalIdentifiers: identifiers, options: nil)
if(assets.count <= 0){
return UIImage()
}
let asset: PHAsset = assets[0]
var img: UIImage?
let manager = PHImageManager.default()
let options = PHImageRequestOptions()
options.version = .original
options.isSynchronous = true
manager.requestImageData(for: asset, options: options) { data, _, _, _ in
if let data = data {
img = UIImage(data: data)
}
}
return img
}
But it doesn't work.
Related
My app is crashing when I select multiple videos(1+ min each) from photo library because of memory warning. I'm trying to compress videos but still the same issue although compression code is working properly. I want my app to select 5 videos at once and send them in chat. Whatsapp is allowing the user to select 30 videos and send them in chat but my app is crashing because of memory issues after 3 videos only.
I am using "AssetsPickerViewController" lib for multiple pic/video selection.
func assetsPicker(controller: AssetsPickerViewController, selected assets: [PHAsset]) {
self.dismiss(animated: true, completion: nil)
var isImages = false
var mediaData: [Data] = []
let imageManager = PHCachingImageManager.default()
DispatchQueue.main.async {
self.appDelegate.helper.showHUD(withMessage: "Preparing media", withObject: self)
}
autoreleasepool {
for selectedAsset in assets {
if selectedAsset.mediaType == .image {
isImages = true
let option = PHImageRequestOptions()
option.isSynchronous = true
option.isNetworkAccessAllowed = true
imageManager.requestImageData(for: selectedAsset, options: option) { (assetData, assetDataUTI, assetOrientation, assetInfo) in
if let data = assetData {
if let image = UIImage(data: data)?.upOrientation(), let finalData = image.jpegData(compressionQuality: 0.5) {
if let newData = ImageHelper.removeExifData(data: finalData as NSData) {
mediaData.append(newData as Data)
self.checkFileStatus(mediaCount: mediaData.count, assetsCount: assets.count, data: mediaData, isImages: isImages)
} else {
mediaData.append(finalData)
self.checkFileStatus(mediaCount: mediaData.count, assetsCount: assets.count, data: mediaData, isImages: isImages)
}
} else {
mediaData.append(data)
self.checkFileStatus(mediaCount: mediaData.count, assetsCount: assets.count, data: mediaData, isImages: isImages)
}
}
}
} else if selectedAsset.mediaType == .video {
let options: PHVideoRequestOptions = PHVideoRequestOptions()
options.isNetworkAccessAllowed = true
options.deliveryMode = .fastFormat
self.convertVideo(phAsset: selectedAsset) { (data) in
if let finalData = data {
mediaData.append(finalData)
self.checkFileStatus(mediaCount: mediaData.count, assetsCount: assets.count, data: mediaData, isImages: isImages)
} else {
}
}
}
}
}
}
func compressVideo(inputURL: URL, outputURL: URL, handler:#escaping (_ exportSession: AVAssetExportSession?)-> Void) {
let urlAsset = AVURLAsset(url: inputURL, options: nil)
guard let exportSession = AVAssetExportSession(asset: urlAsset, presetName: AVAssetExportPresetMediumQuality) else {
handler(nil)
return
}
exportSession.outputURL = outputURL
exportSession.outputFileType = AVFileType.mp4
exportSession.shouldOptimizeForNetworkUse = true
exportSession.exportAsynchronously { () -> Void in
handler(exportSession)
}
}
I am trying to fetch all photos from my device, and show them in a collection:
var allPhotos : PHFetchResult<PHAsset>? = nil
PHPhotoLibrary.requestAuthorization { status in
switch status {
case .authorized:
let fetchOptions = PHFetchOptions()
self.allPhotos = PHAsset.fetchAssets(with: .image, options: fetchOptions)
print("Found \(allPhotos.count) assets")
case .denied, .restricted:
print("Not allowed")
case .notDetermined:
// Should not see this when requesting
print("Not determined yet")
}
}
This code fetches all the photos from the device and as well as from iCloud and then stores them into allPhotos array. But when I am trying to set them using this function in collection view cell, I am only getting photos from the device. The other photos cell is empty. Converting images with this function:
func getUIImage(asset: PHAsset) -> UIImage? {
var img: UIImage?
let manager = PHImageManager.default()
let options = PHImageRequestOptions()
options.version = .original
options.isSynchronous = true
manager.requestImageData(for: asset, options: options) { data, _, _, _ in
if let data = data {
img = UIImage(data: data)
}
}
return img
}
And for some images I am getting this error "Failed to load image data for asset " for those empty cells. Can someone explain why it is not fetching iCloud images?
Because the following code is a closure.
manager.requestImageData(for: asset, options: options) { data, _, _, _ in
if let data = data {
img = UIImage(data: data)
}
}
And closures execute on background thread and are not inline. Closure execution may take time and might not return data instantaneously.
What you could try is creating a closure callback for image to load.
func getUIImage(asset: PHAsset, complition : #escaping ((_ img:UIImage?,_ error:String?) -> Void)){
let manager = PHImageManager.default()
let options = PHImageRequestOptions()
options.version = .original
options.isSynchronous = true
manager.requestImageData(for: asset, options: options) { data, _, _, _ in
if let data = data,
let img = UIImage(data: data){
complition(img,nil)
}else{
complition(nil,"Something went wrong")
}
}
}
You can try this. Hope this help.
Try to add it in requestOptions:
options.isNetworkAccessAllowed = true
It helped me
I am trying to display an image from the local library in another view using its url like so...
let myImgObj = messagesFromTheDisk[indexPath.row].images
print(myImgObj)
print(myImgObj[0].url)
Printing myImgObj gives [MyApp.ProductImage(url: assets-library://asset/asset.PNG?id=A636DA16-8C7C-4CC5-ADB3-E944DC24BDA1&ext=PNG)] and printing myImgObj[0].url gives assets-library://asset/asset.PNG?id=A636DA16-8C7C-4CC5-ADB3-E944DC24BDA1&ext=PNG
But when I try to show display this image its showing as null though there seems to be an url. I'm using the library SDWebImage to display the image like so...
cell.productImageView.sd_setImage(with: myImgObj[0].url, placeholderImage:UIImage(named: "sampleImg"))
What am I doing wrong...?
I set the image libary url nil and use the contentsOfFile to not catche the images in the disk so that immediate loading of images will be implemented and it works.However you must put the images not in .xcassettes folder but a separate group folder.
let bundlePath = Bundle.main.path(forResource: myImgObj, ofType: "jpg")
imageView.sd_setImage(with: nil, placeholderImage: UIImage(contentsOfFile: bundlePath!))
This is without SDWebImage as I don't it need for PHAssets. I am fetching assests from library from my custom album like this :
func fetchCustomAlbumPhotos( completion : (_ array : [PHAsset]) -> Void)
{
var assetCollection = PHAssetCollection()
var photoAssets = PHFetchResult<AnyObject>()
var arrayOfPHAsset = [PHAsset]()
let fetchOptions = PHFetchOptions()
fetchOptions.predicate = NSPredicate(format: "title = %#", CustomAlbum.albumName)
let collection:PHFetchResult = PHAssetCollection.fetchAssetCollections(with: .album, subtype: .any, options: fetchOptions)
if let _:AnyObject = collection.firstObject{
assetCollection = collection.firstObject!
}else {
completion([])
}
_ = collection.count
photoAssets = PHAsset.fetchAssets(in: assetCollection, options: nil) as! PHFetchResult<AnyObject>
photoAssets.enumerateObjects({(object: AnyObject!,
count: Int,
stop: UnsafeMutablePointer<ObjCBool>) in
if object is PHAsset{
let asset = object as! PHAsset
arrayOfPHAsset.append(asset)
}
})
completion(arrayOfPHAsset)
}
This is how I call it in my ViewController's viewDidLoad() method:
CustomAlbum().fetchCustomAlbumPhotos { (array) in
self.arrayOfPHAsset = array
self.collectionView.reloadData()
}
From that function i get arrayOfPHAsset and this is how I am displaying them in my collection view cellForItemAt method:
let asset : PHAsset = arrayOfPHAsset[indexPath.item]
Or convert your url to PHAsset
self.imageURL = (info[UIImagePickerControllerReferenceURL] as? NSURL)!
let asset = PHAsset.fetchAssetsWithALAssetURLs([imageUrl], options: nil).firstObject as! PHAsset
And request Image for that PHAsset
if asset.mediaType == .video{
cell.videoIndicatorImage.isHidden = false
}
cell.userImagesStoredInAlbum.alpha = 0
let imageSize = CGSize(width: asset.pixelWidth,height: asset.pixelHeight)
DispatchQueue.main.async {
PHImageManager.default().requestImage(for: asset, targetSize: imageSize, contentMode: .aspectFill, options: nil, resultHandler: {(result, info)in
if result != nil {
cell.userImagesStoredInAlbum.image = result
UIView .animate(withDuration: 0.5, delay: 0, options: .curveEaseIn, animations: {
cell.userImagesStoredInAlbum.alpha = 1
}, completion: { (succes) in
})
}
})
}
guys.
I am getting images URL from photos by using below code.
func getAllImagesURL() -> [URL]
{
var arr_URL = [URL]()
for index in 0..<fetchResult.count
{
imgManager.requestImageData(for: fetchResult.object(at: index) as PHAsset, options: requestOptions, resultHandler: { (imagedata, dataUTI, orientation, info) in
if let fileName = (info?["PHImageFileURLKey"] as? URL)
{
//do sth with file name
arr_URL.append(fileName)
}
})
}
return arr_URL
}
By using this URL key I want to get the image from photos.I have searched and found below code.But it still not working.
func getImage(assetUrl: URL) -> UIImage? {
let asset = PHAsset.fetchAssets(withALAssetURLs: [assetUrl], options: nil)
guard let result = asset.firstObject else {
return nil
}
var assetImage: UIImage?
let options = PHImageRequestOptions()
options.isSynchronous = true
PHImageManager.default().requestImage(for: result, targetSize: UIScreen.main.bounds.size, contentMode: PHImageContentMode.aspectFill, options: options) { image, info in
assetImage = image
}
return assetImage
}
It returns nil.So please help me.How to get the image by using URL key.
Thanks in Advance..
In the getImage(assetUrl: URL) -> UIImage? method, you are using
let asset = PHAsset.fetchAssets(withALAssetURLs: [assetUrl], options: nil)
here assetUrl is the url that should be taken from if you are using the AssetsLibrary. This library is deprecated from iOS 9.0 onwards. We have to use Photos library instead.
BTW, you are already getting all the images(data) in getAllImageURLs() method. Just convert that method to get all the images and process those images as required. You can use the below method to get all the images.
func getAllImages() -> [UIImage]?
{
let imgManager = PHImageManager.default()
let requestOptions = PHImageRequestOptions()
requestOptions.isSynchronous = true
let fetchOptions = PHFetchOptions()
fetchOptions.sortDescriptors = [NSSortDescriptor(key:"creationDate", ascending: true)]
let fetchResult = PHAsset.fetchAssets(with: PHAssetMediaType.image, options: fetchOptions)
var allImages = [UIImage]()
for index in 0..<fetchResult.count
{
let asset = fetchResult.object(at: index) as PHAsset
imgManager.requestImage(for: asset, targetSize: UIScreen.main.bounds.size, contentMode: .aspectFill, options: requestOptions, resultHandler: { (uiimage, info) in
allImages.append(uiimage!)
})
}
return allImages
}
NOTE: tweak this method as per your requirements.
I’ve encountered a strange behavior:
An imagePicker returns a PHAsset, and I do the Following and manage to present an imageView with image from the data:
asset.requestContentEditingInput(with: PHContentEditingInputRequestOptions()) { (input, _) in
let url = input?.fullSizeImageURL
let imgV = UIImageView()
let test_url = URL(string: (url?.absoluteString)!)
print("><><><^^^><><>\(test_url)")
//prints: ><><><^^^><><>Optional(file:///var/mobile/Media/DCIM/107APPLE/IMG_7242.JPG)
let data = NSData(contentsOf: test_url! as URL)
imgV.image = UIImage(data: data! as Data)
imgV.backgroundColor = UIColor.cyan
att.imageLocalURL = url?.absoluteString//// saving the string to use in the other class
imgV.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
self.view.addSubview(imgV) /// just to test that the file exists and can produce an image
However when I do the following in another class:
if((NSURL( string: self.attachment.imageLocalURL! ) as URL!).isFileURL)// checking if is Local URL
{
let test_url = URL(string: self.attachment.imageLocalURL!) // reading the value stored from before
print("><><><^^^><><>\(test_url)")
//prints :><><><^^^><><>Optional(file:///var/mobile/Media/DCIM/107APPLE/IMG_7242.JPG)
let data = NSData(contentsOf: test_url! as URL)
imageView.image = UIImage(data: data! as Data)
}
The data is nil! What am I doing wrong, the String for URL is identical in both cases!
PHAsset objects should be accessed via the PHImageManager class. If you want to load the image synchronously I recommend you do something like this:
func getImage(assetUrl: URL) -> UIImage? {
let asset = PHAsset.fetchAssets(withALAssetURLs: [assetUrl], options: nil)
guard let result = asset.firstObject else {
return nil
}
var assetImage: UIImage?
let options = PHImageRequestOptions()
options.isSynchronous = true
PHImageManager.default().requestImage(for: result, targetSize: UIScreen.main.bounds.size, contentMode: PHImageContentMode.aspectFill, options: options) { image, info in
assetImage = image
}
return assetImage
}
You could even write a UIImageView extension to load the image directly from a PHAsset url.
var images:NSMutableArray = NSMutableArray() //hold the fetched images
func fetchPhotos ()
{
images = NSMutableArray()
//totalImageCountNeeded = 3
self.fetchPhotoAtIndexFromEnd(0)
}
func fetchPhotoAtIndexFromEnd(index:Int)
{
let status : PHAuthorizationStatus = PHPhotoLibrary.authorizationStatus()
if status == PHAuthorizationStatus.Authorized
{
let imgManager = PHImageManager.defaultManager()
let requestOptions = PHImageRequestOptions()
requestOptions.synchronous = true
let fetchOptions = PHFetchOptions()
fetchOptions.sortDescriptors = [NSSortDescriptor(key:"creationDate", ascending: true)]
if let fetchResult: PHFetchResult = PHAsset.fetchAssetsWithMediaType(PHAssetMediaType.Image, options: fetchOptions)
{
if fetchResult.count > 0
{
imgManager.requestImageForAsset(fetchResult.objectAtIndex(fetchResult.count - 1 - index) as! PHAsset, targetSize: CGSizeMake(self.img_CollectionL.frame.size.height/3, self.img_CollectionL.frame.size.width/3), contentMode: PHImageContentMode.AspectFill, options: requestOptions, resultHandler: { (image, _) in
//self.images.addObject(image!)
if image != nil
{
self.images.addObject(image!)
}
if index + 1 < fetchResult.count && self.images.count < 20 //self.totalImageCountNeeded
{
self.fetchPhotoAtIndexFromEnd(index + 1)
}
else
{
}
})
self.img_CollectionL.reloadData()
}
}
}
}