I have a collectionView running in my project that fetches iPhone photo library by using Photos framework successfully.I am trying to ascending the photo collectionView from last photo.I am using the following code....
func getAllPhotosInfo() {
photoAssets = []
let options = PHFetchOptions()
options.sortDescriptors = [
NSSortDescriptor(key: "creationDate", ascending: false)
]
let assets: PHFetchResult = PHAsset.fetchAssetsWithMediaType(.Image, options: options)
assets.enumerateObjectsUsingBlock { (asset, index, stop) -> Void in
self.photoAssets.append(asset as! PHAsset)
}
print(photoAssets)
}
//PHAsset to image conversion
import UIKit
import Photos
class CollectionViewCell: UICollectionViewCell {
var imageManager: PHImageManager?
#IBOutlet weak var photoImageView: UIImageView!
var imageAsset: PHAsset? {
didSet {
self.imageManager?.requestImageForAsset(self.imageAsset!, targetSize: CGSize(width: 320, height: 320), contentMode: .AspectFill, options: nil) { image, info in
self.photoImageView.image = image
}
}
}
}
I was playing with the Photos framework doc and I found the answer.here is the working code....
var photoAssets = [PHAsset]()
func getAllPhotosInfo() {
photoAssets = []
let options = PHFetchOptions()
options.sortDescriptors = [ NSSortDescriptor(key: "creationDate", ascending: false) ]
options.predicate = NSPredicate(format: "mediaType = %i", PHAssetMediaType.Image.rawValue)
let assetCollections = PHAssetCollection.fetchAssetCollectionsWithType(PHAssetCollectionType.SmartAlbum, subtype: PHAssetCollectionSubtype.SmartAlbumUserLibrary, options: nil)
for i in 0 ..< assetCollections.count
{
if let assetCollection = assetCollections[i] as? PHAssetCollection
{
images = PHAsset.fetchAssetsInAssetCollection(assetCollection, options: options)
print(images.count)
return
}
}
}
Related
I am trying to display a collection view of photo albums on the device. I am able to get the title but not sure how to get the Albums photo and set to the cell.
This is what I have:
import UIKit
import Photos
class PhotoAlbumViewController: UICollectionViewController {
var albumList: PHFetchResult<PHAssetCollection>! = nil
override func viewDidLoad() {
super.viewDidLoad()
albumList = PHAssetCollection.fetchAssetCollections(with: .album, subtype: .albumRegular, options: nil)
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
albumList.count
}
override func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
let album = albumList.object(at: indexPath.row)
if let label = cell.viewWithTag(4000) as? UILabel {
label.text = album.localizedTitle
}
}
}
I have a UI Label in my storyboard and label.text = album.localizedTitle sets the Album title correctly. Not sure how to get the image and set it to my Image component.
You can try this to get all ALBUM names from Photo Library
func get_All_Albums()
{
DispatchQueue.global(qos: .userInteractive).async
{
let fetchOptions = PHFetchOptions()
fetchOptions.predicate = NSPredicate(format: "mediaType = %d", PHAssetMediaType.image.rawValue)
let smartAlbums = PHAssetCollection.fetchAssetCollections(with: .smartAlbum, subtype: .any, options: nil)
let customAlbums = PHAssetCollection.fetchAssetCollections(with: .album, subtype: .any, options: nil)
[smartAlbums, customAlbums].forEach {
$0.enumerateObjects { collection, index, stop in
let requestOptions = PHImageRequestOptions()
requestOptions.isSynchronous = true
requestOptions.deliveryMode = .highQualityFormat
let photoInAlbum = PHAsset.fetchAssets(in: collection, options: fetchOptions)
if let title = collection.localizedTitle
{
if photoInAlbum.count > 0
{
print("\n\n \(title) --- count = \(photoInAlbum.count) \n\n")
}
}
}
}
}
}
& try this to get Photos from a specific Album
func get_Photos_From_Album(albumName: String)
{
var photoLibraryImages = [UIImage]()
var photoLibraryAssets = [PHAsset]()
//whatever you need, you can use UIImage or PHAsset to photos in UICollectionView
DispatchQueue.global(qos: .userInteractive).async
{
let fetchOptions = PHFetchOptions()
fetchOptions.predicate = NSPredicate(format: "mediaType = %d", PHAssetMediaType.image.rawValue)
let smartAlbums = PHAssetCollection.fetchAssetCollections(with: .smartAlbum, subtype: .any, options: nil)
let customAlbums = PHAssetCollection.fetchAssetCollections(with: .album, subtype: .any, options: nil)
[smartAlbums, customAlbums].forEach {
$0.enumerateObjects { collection, index, stop in
let imgManager = PHImageManager.default()
let requestOptions = PHImageRequestOptions()
requestOptions.isSynchronous = true
requestOptions.deliveryMode = .highQualityFormat
let photoInAlbum = PHAsset.fetchAssets(in: collection, options: fetchOptions)
if let title = collection.localizedTitle
{
if photoInAlbum.count > 0
{
print("\n\n \(title) --- count = \(photoInAlbum.count) \n\n")
}
if title == albumName
{
if photoInAlbum.count > 0
{
for i in (0..<photoInAlbum.count).reversed()
{
imgManager.requestImage(for: photoInAlbum.object(at: i) as PHAsset , targetSize: CGSize(width: 150, height: 150), contentMode: .aspectFit, options: requestOptions, resultHandler: {
image, error in
if image != nil
{
photoLibraryImages.append(image!)
photoLibraryAssets.append(photoInAlbum.object(at: i))
}
})
}
}
}
}
}
}
}
}
I am using below code for getting videos(URL, Durations, Thumbnails). After Fetching data i am displaying it on CollectionView.
The main problem is that, some video and its thumbnails not match each other. Can any one please tell me how i can fix it, or any other better solution?
Thanks
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! CollectionViewCell
cell.imgV.image = photoLibrary[indexPath.row]
let duration: TimeInterval = videosDuration[indexPath.row] // 2 minutes, 30 seconds
let formatter = DateComponentsFormatter()
formatter.unitsStyle = .positional
formatter.allowedUnits = [ .minute, .second ]
formatter.zeroFormattingBehavior = [ .pad ]
let formattedDuration = formatter.string(from: duration)
cell.duration.text = "\(String(describing: formattedDuration!))"
return cell
}
func grabPhotos(){
let imgManager = PHImageManager.default()
let requestOptions = PHImageRequestOptions()
requestOptions.isSynchronous = false
requestOptions.deliveryMode = .highQualityFormat
let fetchOptions = PHFetchOptions()
fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
if let fetchResult : PHFetchResult = PHAsset.fetchAssets(with: .video, options: fetchOptions) {
if fetchResult.count > 0 {
for i in 0..<fetchResult.count{
//Used for fetch Image//
imgManager.requestImage(for: fetchResult.object(at: i) as PHAsset , targetSize: CGSize(width: 100, height: 100), contentMode: .aspectFill, options: requestOptions, resultHandler: {
image, error in
let imageOfVideo = image! as UIImage
self.photoLibrary.append(imageOfVideo)
})
//Used for fetch Video//
imgManager.requestAVAsset(forVideo: fetchResult.object(at: i) as PHAsset, options: PHVideoRequestOptions(), resultHandler: {(avAsset, audioMix, info) -> Void in
if let asset = avAsset as? AVURLAsset {
self.videoURL.append(asset.url)
let duration : CMTime = asset.duration
let durationInSecond = CMTimeGetSeconds(duration)
self.videosDuration.append(durationInSecond)
}
})
}
}
else{
//showAllertToImportImage()//A function to show alert
}
}
}
Since photos are being fetched asynchronously so the order is not maintained in the images and videos array, so to get the corresponding image for the video you can use dictionaries to store the result
I have made some modifications in your code, please check
var imageDictionary = [String: AnyObject]()
var videoDictionary = [String: AnyObject]()
func fetchData(){
let imgManager = PHImageManager.default()
let requestOptions = PHImageRequestOptions()
requestOptions.isSynchronous = false
requestOptions.deliveryMode = .highQualityFormat
let fetchOptions = PHFetchOptions()
fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
if let fetchResult : PHFetchResult = PHAsset.fetchAssets(with: .video, options: fetchOptions) {
if fetchResult.count > 0 {
for i in 0..<fetchResult.count{
//Used for fetch Image//
imgManager.requestImage(for: fetchResult.object(at: i) as PHAsset , targetSize: CGSize(width: 100, height: 100), contentMode: .aspectFill, options: requestOptions, resultHandler: {
image, error in
let imageOfVideo = image! as UIImage
//self.photoLibrary.append(imageOfVideo)
let key = "\(i)"
self.imageDictionary[key] = imageOfVideo
})
//Used for fetch Video//
imgManager.requestAVAsset(forVideo: fetchResult.object(at: i) as PHAsset, options: PHVideoRequestOptions(), resultHandler: {(avAsset, audioMix, info) -> Void in
if let asset = avAsset as? AVURLAsset {
//let videoData = NSData(contentsOf: asset.url)
self.videoURL.append(asset.url)
//print(asset.url)
let duration : CMTime = asset.duration
let durationInSecond = CMTimeGetSeconds(duration)
//self.videosDuration.append(durationInSecond)
//print(durationInSecond)
let key = "\(i)"
self.videoDictionary[key] = ["VideoURL" : asset.url, "VideoDuration" : durationInSecond]
}
})
}
}
else{
//showAllertToImportImage()//A function to show alert
}
}
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.imageDictionary.count
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
//You can access the values like this....
let key = "\(indexPath.row)"
cell.image = self.imageDictionary[key] as? UIImage
let singleVideo = self.videoDictionary[key] as? [String: AnyObject]
cell.videoURL = singleVideo["VideoURL"] as? URL
cell.videoDuration = singleVideo["VideoDuration"] as? CMTime
}
I have this function. This function initializes all the media files for gallery view.
func initMediaFiles() {
var assets: [PHAsset] = []
let options = PHFetchOptions()
options.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
options.fetchLimit = 1000
let results = PHAsset.fetchAssets(with: .image, options: options)
results.enumerateObjects({ (object, _, _) in
if let asset = object as? PHAsset {
assets.append(asset)
}
})
SCPAsset.imageManager.startCachingImages(for: assets, targetSize: CGSize(width: 110.0, height: 147.0),contentMode: .aspectFill, options: nil)
for asset in assets {
let scpAsset = SCPAsset(initWithPHAsset: asset)
scpAsset.inspectionUUID = self.inspectionId!
self.mediaAssets.append(scpAsset)
}
}
I am not able to see why I am getting like 10,000 memory leak every time i load the gallery view.
Here's something I've done in a project that might help:
Some properties:
let requestOptions = PHImageRequestOptions()
let fetchOptions = PHFetchOptions()
let cachingImageManager = PHCachingImageManager()
var assets: [PHAsset] = [] {
willSet {
cachingImageManager.stopCachingImagesForAllAssets()
}
didSet {
cachingImageManager.startCachingImagesForAssets(assets,
targetSize: PHImageManagerMaximumSize,
contentMode: .AspectFit,
options: self.requestOptions
)
}
}
func fetchPhotos() {
requestOptions.resizeMode = PHImageRequestOptionsResizeMode.Exact
requestOptions.version = .Current
requestOptions.deliveryMode = PHImageRequestOptionsDeliveryMode.HighQualityFormat
requestOptions.synchronous = true
fetchOptions.sortDescriptors = [NSSortDescriptor(key:"creationDate", ascending: false)]
if #available(iOS 9.0, *) {
fetchOptions.fetchLimit = 50
} else {
// Fallback on earlier versions
}
fetchResults = PHAsset.fetchAssetsWithMediaType(PHAssetMediaType.Image, options: fetchOptions)
guard let fetchResults = fetchResults else {
print("No PHAssets")
return
}
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0)) { [weak self] in
fetchResults.enumerateObjectsUsingBlock{ object, index, stop in
let asset = object as! PHAsset
self?.assets.append(asset)
}
dispatch_async(dispatch_get_main_queue()) {
self?.photoCollectionView.reloadData()
}
}
}
Is there a way to add everything from your camera roll to an array?
import Photos
class MyVC: UIViewController
{
var images:NSMutableArray! // Array for storing images
func fetchPhotos () {
images = NSMutableArray()
self.fetchPhotoAtIndexFromEnd(0)
}
func getImageAtIndex(index:Int) {
let imgManager = PHImageManager.defaultManager()
var requestOptions = PHImageRequestOptions()
requestOptions.synchronous = true // if just return thumbnail not img
// optionally sort the images by creation date
var fetchOptions = PHFetchOptions()
fetchOptions.sortDescriptors = [NSSortDescriptor(key:"creationDate", ascending: false)]
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: view.frame.size, contentMode: PHImageContentMode.AspectFill, options: requestOptions, resultHandler: { (image, _) in
// Add img to images array
self.images.addObject(image)
if index + 1 < fetchResult.count {
self.fetchPhotoAtIndexFromEnd(index + 1)
} else {
println("Completed array: \(self.images)")
}
})
}
}
}
How to fetch all images from custom Photo Album?
var fetchOptions = PHFetchOptions()
fetchOptions.sortDescriptors = [NSSortDescriptor(key:"creationDate", ascending: false)]
fetchOptions.predicate = NSPredicate(format: "mediaType = %d", PHAssetMediaType.Image.rawValue)
let allImages:PHFetchResult = PHAsset.fetchKeyAssetsInAssetCollection(albumList[index].collection, options: fetchOptions)
This code block is fetching just a few of them.
Thanks.
-> albumList[index].collection 's type is PHAssetCollection
For Swift 4
using this answer https://stackoverflow.com/a/28904792/4795651 edited a little for myself.
import Photos
func fetchCustomAlbumPhotos()
{
let albumName = "Album Name Here"
var assetCollection = PHAssetCollection()
var albumFound = Bool()
var photoAssets = PHFetchResult<AnyObject>()
let fetchOptions = PHFetchOptions()
fetchOptions.predicate = NSPredicate(format: "title = %#", albumName)
let collection:PHFetchResult = PHAssetCollection.fetchAssetCollections(with: .album, subtype: .any, options: fetchOptions)
if let firstObject = collection.firstObject{
//found the album
assetCollection = firstObject
albumFound = true
}
else { albumFound = false }
_ = collection.count
photoAssets = PHAsset.fetchAssets(in: assetCollection, options: nil) as! PHFetchResult<AnyObject>
let imageManager = PHCachingImageManager()
photoAssets.enumerateObjects{(object: AnyObject!,
count: Int,
stop: UnsafeMutablePointer<ObjCBool>) in
if object is PHAsset{
let asset = object as! PHAsset
print("Inside If object is PHAsset, This is number 1")
let imageSize = CGSize(width: asset.pixelWidth,
height: asset.pixelHeight)
/* For faster performance, and maybe degraded image */
let options = PHImageRequestOptions()
options.deliveryMode = .fastFormat
options.isSynchronous = true
imageManager.requestImage(for: asset,
targetSize: imageSize,
contentMode: .aspectFill,
options: options,
resultHandler: {
(image, info) -> Void in
self.photo = image!
/* The image is now available to us */
self.addImgToArray(uploadImage: self.photo!)
print("enum for image, This is number 2")
})
}
}
}
func addImgToArray(uploadImage:UIImage)
{
self.images.append(uploadImage)
}
For Swift 2.1
import Photos
func FetchCustomAlbumPhotos()
{
var albumName = "SwiftAlbum"
var assetCollection = PHAssetCollection()
var albumFound = Bool()
var photoAssets = PHFetchResult()
let fetchOptions = PHFetchOptions()
fetchOptions.predicate = NSPredicate(format: "title = %#", albumName)
let collection:PHFetchResult = PHAssetCollection.fetchAssetCollectionsWithType(.Album, subtype: .Any, options: fetchOptions)
if let first_Obj:AnyObject = collection.firstObject{
//found the album
assetCollection = collection.firstObject as! PHAssetCollection
albumFound = true
}
else { albumFound = false }
var i = collection.count
photoAssets = PHAsset.fetchAssetsInAssetCollection(assetCollection, options: nil)
let imageManager = PHCachingImageManager()
// let imageManager = PHImageManager.defaultManager()
photoAssets.enumerateObjectsUsingBlock{(object: AnyObject!,
count: Int,
stop: UnsafeMutablePointer<ObjCBool>) in
if object is PHAsset{
let asset = object as! PHAsset
print("Inside If object is PHAsset, This is number 1")
let imageSize = CGSize(width: asset.pixelWidth,
height: asset.pixelHeight)
/* For faster performance, and maybe degraded image */
let options = PHImageRequestOptions()
options.deliveryMode = .FastFormat
options.synchronous = true
imageManager.requestImageForAsset(asset,
targetSize: imageSize,
contentMode: .AspectFill,
options: options,
resultHandler: {
(image, info) -> Void in
self.photo = image!
/* The image is now available to us */
self.addImgToArray(self.photo)
print("enum for image, This is number 2")
})
}
}
}
func addImgToArray(uploadImage:UIImage)
{
self.images.append(uploadImage)
}
This is a custom album, so what you've got to do is to add it to your code in the middle:
import Photos
class CustomPhotoAlbum {
static let albumName = "Album"
static let sharedInstance = CustomPhotoAlbum()
var assetCollection: PHAssetCollection!
init() {
func fetchAssetCollectionForAlbum() -> PHAssetCollection! {
let fetchOptions = PHFetchOptions()
fetchOptions.predicate = NSPredicate(format: "title = %#", CustomPhotoAlbum.albumName)
let collection = PHAssetCollection.fetchAssetCollections(with: .album, subtype: .any, options: fetchOptions)
if let firstObject: AnyObject = collection.firstObject {
return collection.firstObject as? PHAssetCollection
}
return nil
}
if let assetCollection = fetchAssetCollectionForAlbum() {
self.assetCollection = assetCollection
return
}
PHPhotoLibrary.shared().performChanges({
PHAssetCollectionChangeRequest.creationRequestForAssetCollection(withTitle: CustomPhotoAlbum.albumName)
}) { success, _ in
if success {
self.assetCollection = fetchAssetCollectionForAlbum()
}
}
}
func saveImage(image: UIImage) {
if assetCollection == nil {
return // If there was an error upstream, skip the save.
}
PHPhotoLibrary.shared().performChanges({
let assetChangeRequest = PHAssetChangeRequest.creationRequestForAsset(from: image)
let assetPlaceholder = assetChangeRequest.placeholderForCreatedAsset
let albumChangeRequest = PHAssetCollectionChangeRequest(for: self.assetCollection)
albumChangeRequest?.addAssets([assetPlaceholder] as NSFastEnumeration)
}, completionHandler: nil)
}
}
Then to fetch all the pics in your album use this code:
func getAlbum(title: String, completionHandler: #escaping (PHAssetCollection?) -> ()) {
DispatchQueue.global(qos: .background).async { [weak self] in
let fetchOptions = PHFetchOptions()
fetchOptions.predicate = NSPredicate(format: "title = %#", title)
let collections = PHAssetCollection.fetchAssetCollections(with: .album, subtype: .any, options: fetchOptions)
if let album = collections.firstObject {
completionHandler(album)
} else {
self?.createAlbum(withTitle: title, completionHandler: { (album) in
completionHandler(album)
})
}
}
}
To call this function use this example:
getAlbum(title: "Album") { [weak self] assetCollection in
guard let this = self else { return }
guard assetCollection != nil else { return }
print(assetCollection!)
}
If you want to create a custom album use this function:
func createAlbum(withTitle title: String, completionHandler: #escaping (PHAssetCollection?) -> ()) {
DispatchQueue.global(qos: .background).async {
var placeholder: PHObjectPlaceholder?
PHPhotoLibrary.shared().performChanges({
let createAlbumRequest = PHAssetCollectionChangeRequest.creationRequestForAssetCollection(withTitle: title)
placeholder = createAlbumRequest.placeholderForCreatedAssetCollection
}, completionHandler: { (created, error) in
var album: PHAssetCollection?
if created {
let collectionFetchResult = placeholder.map { PHAssetCollection.fetchAssetCollections(withLocalIdentifiers: [$0.localIdentifier], options: nil) }
album = collectionFetchResult?.firstObject
}
completionHandler(album)
})
}
}
This function will be called in the getAlbum, if the album isn't created.
Try this to get Photos from a specific Album
func get_Photos_From_Album(albumName: String)
{
var photoLibraryImages = [UIImage]()
var photoLibraryAssets = [PHAsset]()
//whatever you need, you can use UIImage or PHAsset to photos in UICollectionView
DispatchQueue.global(qos: .userInteractive).async
{
let fetchOptions = PHFetchOptions()
fetchOptions.predicate = NSPredicate(format: "mediaType = %d", PHAssetMediaType.image.rawValue)
let smartAlbums = PHAssetCollection.fetchAssetCollections(with: .smartAlbum, subtype: .any, options: nil)
let customAlbums = PHAssetCollection.fetchAssetCollections(with: .album, subtype: .any, options: nil)
[smartAlbums, customAlbums].forEach {
$0.enumerateObjects { collection, index, stop in
let imgManager = PHImageManager.default()
let requestOptions = PHImageRequestOptions()
requestOptions.isSynchronous = true
requestOptions.deliveryMode = .highQualityFormat
let photoInAlbum = PHAsset.fetchAssets(in: collection, options: fetchOptions)
if let title = collection.localizedTitle
{
if photoInAlbum.count > 0
{
print("\n\n \(title) --- count = \(photoInAlbum.count) \n\n")
}
if title == albumName
{
if photoInAlbum.count > 0
{
for i in (0..<photoInAlbum.count).reversed()
{
imgManager.requestImage(for: photoInAlbum.object(at: i) as PHAsset , targetSize: CGSize(width: 150, height: 150), contentMode: .aspectFit, options: requestOptions, resultHandler: {
image, error in
if image != nil
{
photoLibraryImages.append(image!)
photoLibraryAssets.append(photoInAlbum.object(at: i))
}
})
}
}
}
}
}
}
}
}
SWIFT 4
func FetchCustomAlbumPhotos()
{
let albumName = "AlbumName"
var assetCollection = PHAssetCollection()
var albumFound = Bool()
var photoAssets = PHFetchResult<AnyObject>()
let fetchOptions = PHFetchOptions()
fetchOptions.predicate = NSPredicate(format: "title = %#", albumName)
let collection:PHFetchResult = PHAssetCollection.fetchAssetCollections(with: .album, subtype: .any, options: fetchOptions)
if let _:AnyObject = collection.firstObject{
//found the album
assetCollection = collection.firstObject!
albumFound = true
}
else { albumFound = false }
_ = collection.count
photoAssets = PHAsset.fetchAssets(in: assetCollection, options: nil) as! PHFetchResult<AnyObject>
let imageManager = PHCachingImageManager()
// let imageManager = PHImageManager.defaultManager()
photoAssets.enumerateObjects{(object: AnyObject!,
count: Int,
stop: UnsafeMutablePointer<ObjCBool>) in
if object is PHAsset{
let asset = object as! PHAsset
print("Inside If object is PHAsset, This is number 1")
let imageSize = CGSize(width: asset.pixelWidth,
height: asset.pixelHeight)
/* For faster performance, and maybe degraded image */
let options = PHImageRequestOptions()
options.deliveryMode = .fastFormat
options.isSynchronous = true
imageManager.requestImage(for: asset,
targetSize: imageSize,
contentMode: .aspectFit ,
options: options,
resultHandler: {
(image, info) -> Void in
print("Image \(String(describing: image))")
//self.photo = image!
/* The image is now available to us */
//self.addImgToArray(self.photo)
print("enum for image, This is number 2")
})
}
}
}