I'm new in Swift. In the following code, it retrieve photos and put them in an array. Now I want to show them in imageviews. How can I do it?
I mean how ,for example, show an element of the array in an imageview.
var list :[PHAsset] = []
PHPhotoLibrary.requestAuthorization { (status) in
switch status
{
case .authorized:
print("Good to proceed")
let fetchOptions = PHFetchOptions()
let allPhotos = PHAsset.fetchAssets(with: .image, options: fetchOptions)
print(allPhotos.count)
allPhotos.enumerateObjects({ (object, count, stop) in
list.append(object)
})
print("Found \(allPhotos.count) images")
case .denied, .restricted:
print("Not allowed")
case .notDetermined:
print("Not determined yet")
}
Another question is: When I call this function it seems it execute Asynchronously. I mean code lines after calling the function will be executed early. Is this because of requestAuthorization?
Try this: imageView.image = convertImageFromAsset(list[0])
func convertImageFromAsset(asset: PHAsset) -> UIImage {
let manager = PHImageManager.default()
let option = PHImageRequestOptions()
var image = UIImage()
option.isSynchronous = true
manager.requestImage(for: asset, targetSize: PHImageManagerMaximumSize, contentMode: .aspectFit, options: option, resultHandler: {(result, info)->Void in
image = result!
})
return image
}
Hope it helps.
You can do like this :-
Create an empty array of type PHAsset :-
fileprivate var imageAssets = [PHAsset]()
Fetch all the images by calling this function :-
func fetchGallaryResources(){
let status = PHPhotoLibrary.authorizationStatus()
if (status == .denied || status == .restricted) {
self.showAlert(cancelTitle: nil, buttonTitles:["OK"], title: "Oops", message:"Access to PHPhoto library is denied.")
return
}else{
PHPhotoLibrary.requestAuthorization { (authStatus) in
if authStatus == .authorized{
let imageAsset = PHAsset.fetchAssets(with: .image, options: nil)
for index in 0..<imageAsset.count{
self.imageAssets.append((imageAsset[index]))
}
}
}
Request for the imageLike this :-
let availableWidth = UIScreen.main.bounds.size.width
let availableHeight = UIScreen.main.bounds.size.height
Take out the image from imageAssets in a loop or single one just like that :-
PHImageManager.default().requestImage(for: imageAssets[0], targetSize: CGSize(width : availableWidth, height : calculatedCellWidth), contentMode: .default, options: nil, resultHandler: { (image, info) in
requestedImageView.image = image
})
Related
I am using UICollectionView for an album. I got the picture to load on iPhone 12. I got it to load about 100 pictures but if there are over 3000 pictures when I scroll the UICollectionView down it is very slow and sometimes the app stops.
I am using this code to get the pictures.
func grabPhotos(){
imageArray = []
let options = PHFetchOptions()
options.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: true)]
let assetsFetchResult = (collection == nil) ? PHAsset.fetchAssets(with: .image, options: options) : PHAsset.fetchAssets(in: collection!, options: nil)
assets = assetsFetchResult.objects(at: IndexSet(integersIn: Range(NSMakeRange(0, assetsFetchResult.count))!))
let imgManager=PHImageManager.default()
let requestOptions=PHImageRequestOptions()
requestOptions.isSynchronous=true
requestOptions.deliveryMode = .highQualityFormat
requestOptions.resizeMode = .exact
requestOptions.version = .original
requestOptions.isNetworkAccessAllowed = true
if assetsFetchResult.count > 0 {
for i in 0..<assetsFetchResult.count{
imgManager.requestImage(for: assetsFetchResult.object(at: i) as PHAsset, targetSize: CGSize(width:150, height: 200) ,contentMode: .aspectFill, options: requestOptions, resultHandler: { (image, error) in
if image != nil {
DispatchQueue.main.async(execute: {
self.imageArray.append(image!)
})
}
})
}
} else {
print("You got no photos.")
}
DispatchQueue.main.async {
print("This is run on the main queue, after the previous code in outer block")
self.categoryView.reloadData()
self.activityIndicator.stopAnimating()
}
}
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've tried to fetch photos from the library. It works, but I just got 3 photos from 9 photos from the library. Here's my code:
let options = PHFetchOptions()
let userAlbums = PHAssetCollection.fetchAssetCollections(with: PHAssetCollectionType.album, subtype: PHAssetCollectionSubtype.any, options: options)
let userPhotos = PHAsset.fetchKeyAssets(in: userAlbums.firstObject!, options: nil)
let imageManager = PHCachingImageManager()
userPhotos?.enumerateObjects({ (object: AnyObject!, count: Int, stop: UnsafeMutablePointer) in
if object is PHAsset {
let obj:PHAsset = object as! PHAsset
let fetchOptions = PHFetchOptions()
fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
fetchOptions.predicate = NSPredicate(format: "mediaType = %d", PHAssetMediaType.image.rawValue)
let options = PHImageRequestOptions()
options.deliveryMode = .fastFormat
options.isSynchronous = true
imageManager.requestImage(for: obj, targetSize: CGSize(width: obj.pixelWidth, height: obj.pixelHeight), contentMode: .aspectFill, options: options, resultHandler: { img, info in
self.images.append(img!)
})
}
})
When I tried images.count, it said 3. Can anyone help me to find my mistake and get all photos? Big thanks!
Try this,
first import photos
import Photos
then declare Array for store photo before viewDidLoad()
var allPhotos : PHFetchResult<PHAsset>? = nil
Now write code for fetch photo in viewDidLoad()
/// Load Photos
PHPhotoLibrary.requestAuthorization { (status) in
switch status {
case .authorized:
print("Good to proceed")
let fetchOptions = PHFetchOptions()
self.allPhotos = PHAsset.fetchAssets(with: .image, options: fetchOptions)
case .denied, .restricted:
print("Not allowed")
case .notDetermined:
print("Not determined yet")
}
}
Now write this code for display image from Array
/// Display Photo
let asset = allPhotos?.object(at: indexPath.row)
self.imageview.fetchImage(asset: asset!, contentMode: .aspectFit, targetSize: self.imageview.frame.size)
// Or Display image in Collection View cell
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = self.collectionView.dequeueReusableCell(withReuseIdentifier: "photoCell", for: indexPath) as! SelectPhotoCell
let asset = allPhotos?.object(at: indexPath.row)
cell.imgPicture.fetchImage(asset: asset!, contentMode: .aspectFit, targetSize: cell.imgPicture.frame.size)
return cell
}
extension UIImageView{
func fetchImage(asset: PHAsset, contentMode: PHImageContentMode, targetSize: CGSize) {
let options = PHImageRequestOptions()
options.version = .original
PHImageManager.default().requestImage(for: asset, targetSize: targetSize, contentMode: contentMode, options: options) { image, _ in
guard let image = image else { return }
switch contentMode {
case .aspectFill:
self.contentMode = .scaleAspectFill
case .aspectFit:
self.contentMode = .scaleAspectFit
}
self.image = image
}
}
}
Swift 5 - Update
First import Photos
Import Photos
Create a variable to hold all the images
var images = [UIImage]()
Main function to grab the assets and request image from asset
fileprivate func getPhotos() {
let manager = PHImageManager.default()
let requestOptions = PHImageRequestOptions()
requestOptions.isSynchronous = false
requestOptions.deliveryMode = .highQualityFormat
// .highQualityFormat will return better quality photos
let fetchOptions = PHFetchOptions()
fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
let results: PHFetchResult = PHAsset.fetchAssets(with: .image, options: fetchOptions)
if results.count > 0 {
for i in 0..<results.count {
let asset = results.object(at: i)
let size = CGSize(width: 700, height: 700)
manager.requestImage(for: asset, targetSize: size, contentMode: .aspectFill, options: requestOptions) { (image, _) in
if let image = image {
self.images.append(image)
self.collectionView.reloadData()
} else {
print("error asset to image")
}
}
}
} else {
print("no photos to display")
}
}
Check first in info.plist that your app is authorized to access photos from library. than Use the below code to access all photos:
PHPhotoLibrary.requestAuthorization { (status) in
switch status {
case .authorized:
print("You Are Authrized To Access")
let fetchOptions = PHFetchOptions()
let allPhotos = PHAsset.fetchAssets(with: .image, options: fetchOptions)
print("Found number of: \(allPhotos.count) images")
case .denied, .restricted:
print("Not allowed")
case .notDetermined:
print("Not determined yet")
}
}
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.