Swift - how to get last taken 3 photos from photo library?
Asked Answered
B

5

35

I need to get and show last taken 3 photos from photo library on viewDidload event without any clicks.

After this step, I should get other photos 3 by 3 when I scroll the ScrollView.

Do you know the proper way to do this with swift? Thanks.

Sample

Babirusa answered 1/2, 2015 at 5:58 Comment(0)
C
59

Here's a solution using the Photos framework available for devices iOS 8+ :

import Photos

class ViewController: UIViewController {

    var images:[UIImage] = []

    func fetchPhotos () {
        // Sort the images by descending creation date and fetch the first 3
        let fetchOptions = PHFetchOptions()
        fetchOptions.sortDescriptors = [NSSortDescriptor(key:"creationDate", ascending: false)]
        fetchOptions.fetchLimit = 3

        // Fetch the image assets
        let fetchResult: PHFetchResult = PHAsset.fetchAssets(with: PHAssetMediaType.image, options: fetchOptions)

        // If the fetch result isn't empty,
        // proceed with the image request
        if fetchResult.count > 0 {
            let totalImageCountNeeded = 3 // <-- The number of images to fetch
            fetchPhotoAtIndex(0, totalImageCountNeeded, fetchResult)
        }
    }

    // Repeatedly call the following method while incrementing
    // the index until all the photos are fetched
    func fetchPhotoAtIndex(_ index:Int, _ totalImageCountNeeded: Int, _ fetchResult: PHFetchResult<PHAsset>) {

        // Note that if the request is not set to synchronous
        // the requestImageForAsset will return both the image
        // and thumbnail; by setting synchronous to true it
        // will return just the thumbnail
        let requestOptions = PHImageRequestOptions()
        requestOptions.isSynchronous = true

        // Perform the image request
        PHImageManager.default().requestImage(for: fetchResult.object(at: index) as PHAsset, targetSize: view.frame.size, contentMode: PHImageContentMode.aspectFill, options: requestOptions, resultHandler: { (image, _) in
            if let image = image {
                // Add the returned image to your array
                self.images += [image]
            }
            // If you haven't already reached the first
            // index of the fetch result and if you haven't
            // already stored all of the images you need,
            // perform the fetch request again with an
            // incremented index
            if index + 1 < fetchResult.count && self.images.count < totalImageCountNeeded {
                self.fetchPhotoAtIndex(index + 1, totalImageCountNeeded, fetchResult)
            } else {
                // Else you have completed creating your array
                print("Completed array: \(self.images)")
            }
        })
    }
}
Cording answered 1/2, 2015 at 8:30 Comment(11)
My app crashes when I do this with a total count of more than 50. Is there any reason you can think of why this would happen? I don't get a runtime error or anything.Elysian
When I get 10 photos using this method, it took about 1 second. Any way to make it faster?Dancette
This method doesn't seem to be fetching Live photos. IS it possible to fetch Live photos too? ThanksSisneros
I used this solution for something I am working on. There is a catch though. When the photos are not downloaded on the device and they are stored in icloud it crashes, is there anyway to skip over photos not downloaded, or attempt to download them then fetch them?Nonresistance
Executing is too longBurin
This solution may not be efficient. Please see answers below.Glorify
@TimChen Yeah, you're right -- I wrote this a few years back and don't know why I included fetchAssetsWithMediaType as part of the iteration. It only needs to be called once. I'll update it soon. Your answer below is partially right, but the problem is that it returns PHAssets and not images as requested by the question.Cording
@TimChen I've updated the answer and would appreciate your input if you have any suggestions. :)Cording
@bkwebhero That's because you're storing 50 images at once in an array -- memory issue.Cording
@LyndseyScott +1 for updating the selected answer.Glorify
This answer helped me. Thanks!Brusque
B
18

Details

  • Xcode 10.2 (10E125), Swift 5

Solution features

  • works in asynchronously and thread/queue safety
  • get albums ( + all photos album)
  • optimized for fast scrolling
  • get images with defined size

Info.plist

Add to Info.plist

<key>NSPhotoLibraryUsageDescription</key>
<string>{bla-bla-bla}</string>

Solution

AtomicArray here: https://mcmap.net/q/15626/-create-thread-safe-array-in-swift

import UIKit
import Photos

enum PhotoAlbumViewModel {
    case regular(id: Int, title: String, count: Int, image: UIImage, isSelected: Bool)
    case allPhotos(id: Int, title: String, count: Int, image: UIImage, isSelected: Bool)

    var id: Int { switch self { case .regular(let params), .allPhotos(let params): return params.id } }
    var count: Int { switch self { case .regular(let params), .allPhotos(let params): return params.count } }
    var title: String { switch self { case .regular(let params), .allPhotos(let params): return params.title } }
}

class PhotoService {

    internal lazy var imageManager = PHCachingImageManager()

    private lazy var queue = DispatchQueue(label: "PhotoService_queue",
                                           qos: .default, attributes: .concurrent,
                                           autoreleaseFrequency: .workItem, target: nil)
    private lazy var getImagesQueue = DispatchQueue(label: "PhotoService_getImagesQueue",
                                                    qos: .userInteractive, attributes: [],
                                                    autoreleaseFrequency: .inherit, target: nil)
    private lazy var thumbnailSize = CGSize(width: 200, height: 200)
    private lazy var imageAlbumsIds = AtomicArray<Int>()
    private let getImageSemaphore = DispatchSemaphore(value: 12)

    typealias AlbumData = (fetchResult: PHFetchResult<PHAsset>, assetCollection: PHAssetCollection?)
    private let _cachedAlbumsDataSemaphore = DispatchSemaphore(value: 1)
    private lazy var _cachedAlbumsData = [Int: AlbumData]()

    deinit {
        print("____ PhotoServiceImpl deinited")
        imageManager.stopCachingImagesForAllAssets()
    }
}

// albums

extension PhotoService {

    private func getAlbumData(id: Int, completion: ((AlbumData?) -> Void)?) {
        _ = _cachedAlbumsDataSemaphore.wait(timeout: .now() + .seconds(3))
        if let cachedAlbum = _cachedAlbumsData[id] {
            completion?(cachedAlbum)
            _cachedAlbumsDataSemaphore.signal()
            return
        } else {
            _cachedAlbumsDataSemaphore.signal()
        }
        var result: AlbumData? = nil
        switch id {
            case 0:
                let fetchOptions = PHFetchOptions()
                fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: true)]
                let allPhotos = PHAsset.fetchAssets(with: .image, options: fetchOptions)
                result = (allPhotos, nil)

            default:
                let collections = getAllAlbumsAssetCollections()
                let id = id - 1
                if  id < collections.count {
                    _fetchAssets(in: collections[id]) { fetchResult in
                        result = (fetchResult, collections[id])
                    }
                }
        }
        guard let _result = result else { completion?(nil); return }
        _ = _cachedAlbumsDataSemaphore.wait(timeout: .now() + .seconds(3))
        _cachedAlbumsData[id] = _result
        _cachedAlbumsDataSemaphore.signal()
        completion?(_result)
    }

    private func getAllAlbumsAssetCollections() -> PHFetchResult<PHAssetCollection> {
        let fetchOptions = PHFetchOptions()
        fetchOptions.sortDescriptors = [NSSortDescriptor(key: "endDate", ascending: true)]
        fetchOptions.predicate = NSPredicate(format: "estimatedAssetCount > 0")
        return PHAssetCollection.fetchAssetCollections(with: .album, subtype: .any, options: fetchOptions)
    }

    func getAllAlbums(completion: (([PhotoAlbumViewModel])->Void)?) {
        queue.async { [weak self] in
            guard let self = self else { return }
            var viewModels = AtomicArray<PhotoAlbumViewModel>()

            var allPhotosAlbumViewModel: PhotoAlbumViewModel?
            let dispatchGroup = DispatchGroup()
            dispatchGroup.enter()
            self.getAlbumData(id: 0) { data in
                   guard let data = data, let asset = data.fetchResult.lastObject else { dispatchGroup.leave(); return }
                self._fetchImage(from: asset, userInfo: nil, targetSize: self.thumbnailSize,
                                     deliveryMode: .fastFormat, resizeMode: .fast) { [weak self] (image, _) in
                                        guard let self = self, let image = image else { dispatchGroup.leave(); return }
                                        allPhotosAlbumViewModel = .allPhotos(id: 0, title: "All Photos",
                                                                             count: data.fetchResult.count,
                                                                             image: image, isSelected: false)
                                    self.imageAlbumsIds.append(0)
                                    dispatchGroup.leave()
                }
            }

            let numberOfAlbums = self.getAllAlbumsAssetCollections().count + 1
            for id in 1 ..< numberOfAlbums {
                dispatchGroup.enter()
                self.getAlbumData(id: id) { [weak self] data in
                    guard let self = self else { return }
                    guard let assetCollection = data?.assetCollection else { dispatchGroup.leave(); return }
                    self.imageAlbumsIds.append(id)
                    self.getAlbumViewModel(id: id, collection: assetCollection) { [weak self] model in
                        guard let self = self else { return }
                        defer { dispatchGroup.leave() }
                        guard let model = model else { return }
                        viewModels.append(model)
                    }
                }
            }

            _ = dispatchGroup.wait(timeout: .now() + .seconds(3))
            var _viewModels = [PhotoAlbumViewModel]()
            if let allPhotosAlbumViewModel = allPhotosAlbumViewModel {
                _viewModels.append(allPhotosAlbumViewModel)
            }
            _viewModels += viewModels.get()
            DispatchQueue.main.async { completion?(_viewModels) }
        }
    }

    private func getAlbumViewModel(id: Int, collection: PHAssetCollection, completion: ((PhotoAlbumViewModel?) -> Void)?) {
        _fetchAssets(in: collection) { [weak self] fetchResult in
            guard let self = self, let asset = fetchResult.lastObject else { completion?(nil); return }
            self._fetchImage(from: asset, userInfo: nil, targetSize: self.thumbnailSize,
                             deliveryMode: .fastFormat, resizeMode: .fast) { (image, nil) in
                                guard let image = image else { completion?(nil); return }
                                completion?(.regular(id: id,
                                                     title: collection.localizedTitle ?? "",
                                                     count: collection.estimatedAssetCount,
                                                     image: image, isSelected: false))
            }
        }
    }
}

// fetch

extension PhotoService {

    fileprivate func _fetchImage(from photoAsset: PHAsset,
                                 userInfo: [AnyHashable: Any]? = nil,
                                 targetSize: CGSize, //= PHImageManagerMaximumSize,
                                 deliveryMode: PHImageRequestOptionsDeliveryMode = .fastFormat,
                                 resizeMode: PHImageRequestOptionsResizeMode,
                                 completion: ((_ image: UIImage?, _ userInfo: [AnyHashable: Any]?) -> Void)?) {
        // guard authorizationStatus() == .authorized else { completion(nil); return }
        let options = PHImageRequestOptions()
        options.resizeMode = resizeMode
        options.isSynchronous = true
        options.deliveryMode = deliveryMode
        imageManager.requestImage(for: photoAsset,
                                  targetSize: targetSize,
                                  contentMode: .aspectFill,
                                  options: options) { (image, info) -> Void in
                                    guard   let info = info,
                                            let isImageDegraded = info[PHImageResultIsDegradedKey] as? Int,
                                            isImageDegraded == 0 else { completion?(nil, nil); return }
                                    completion?(image, userInfo)
        }
    }

    private func _fetchAssets(in collection: PHAssetCollection, completion: @escaping (PHFetchResult<PHAsset>) -> Void) {
        let fetchOptions = PHFetchOptions()
        fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: true)]
        let assets = PHAsset.fetchAssets(in: collection, options: fetchOptions)
        completion(assets)
    }

    private func fetchImage(from asset: PHAsset,
                            userInfo: [AnyHashable: Any]?,
                            targetSize: CGSize,
                            deliveryMode: PHImageRequestOptionsDeliveryMode,
                            resizeMode: PHImageRequestOptionsResizeMode,
                            completion:  ((UIImage?, _ userInfo: [AnyHashable: Any]?) -> Void)?) {
        queue.async { [weak self] in
            self?._fetchImage(from: asset, userInfo: userInfo, targetSize: targetSize,
                              deliveryMode: deliveryMode, resizeMode: resizeMode) { (image, _) in
                                DispatchQueue.main.async { completion?(image, userInfo) }
            }
        }
    }

    func getImage(albumId: Int, index: Int,
                  userInfo: [AnyHashable: Any]?,
                  targetSize: CGSize,
                  deliveryMode: PHImageRequestOptionsDeliveryMode,
                  resizeMode: PHImageRequestOptionsResizeMode,
                  completion:  ((_ image: UIImage?, _ userInfo: [AnyHashable: Any]?) -> Void)?) {
        getImagesQueue.async { [weak self] in
            guard let self = self else { return }
            let indexPath = IndexPath(item: index, section: albumId)
            self.getAlbumData(id: albumId) { data in
                _ = self.getImageSemaphore.wait(timeout: .now() + .seconds(3))
                guard let photoAsset = data?.fetchResult.object(at: index) else { self.getImageSemaphore.signal(); return }
                self.fetchImage(from: photoAsset,
                                userInfo: userInfo,
                                targetSize: targetSize,
                                deliveryMode: deliveryMode,
                                resizeMode: resizeMode) { [weak self] (image, userInfo) in
                                    defer { self?.getImageSemaphore.signal() }
                                    completion?(image, userInfo)
                }
            }
        }
    }
}

Usage

private lazy var photoLibrary = PhotoService()
private var albums = [PhotoAlbumViewModel]()
//....

// Get albums
photoLibrary.getAllAlbums { [weak self] albums in
    self?.albums = albums
    // reload views
}

// Get photo
photoLibrary.getImage(albumId: albums[0].id,
                      index: 1, userInfo: nil,
                      targetSize: CGSize(width: 200, height: 200),
                      deliveryMode: .fastFormat,
                      resizeMode: .fast) { [weak self, weak cell] (image, userInfo) in
                        // reload views
}

Full Sample (collectionView with images from PhotoLibrary)

ViewController.swift

import UIKit
import Photos

class ViewController: UIViewController {

    private weak var collectionView: UICollectionView?
    var collectionViewFlowLayout: UICollectionViewFlowLayout? {
        return collectionView?.collectionViewLayout as? UICollectionViewFlowLayout
    }

    private lazy var photoLibrary = PhotoService()
    private lazy var numberOfElementsInRow = 4

    private lazy var cellIdentifier = "cellIdentifier"
    private lazy var supplementaryViewIdentifier = "supplementaryViewIdentifier"
    private var albums = [PhotoAlbumViewModel]()

    private lazy var cellsTags = [IndexPath: Int]()
    private lazy var tagKey = "cellTag"
    private lazy var thumbnailImageSize = CGSize(width: 200, height: 200)

    override func viewDidLoad() {

        let collectionViewFlowLayout = UICollectionViewFlowLayout()
        collectionViewFlowLayout.minimumLineSpacing = 5
        collectionViewFlowLayout.minimumInteritemSpacing = 5
        let _numberOfElementsInRow = CGFloat(numberOfElementsInRow)
        let allWidthBetwenCells = _numberOfElementsInRow == 0 ? 0 : collectionViewFlowLayout.minimumInteritemSpacing*(_numberOfElementsInRow-1)
        let width = (view.frame.width - allWidthBetwenCells)/_numberOfElementsInRow
        collectionViewFlowLayout.itemSize = CGSize(width: width, height: width)
        collectionViewFlowLayout.headerReferenceSize = CGSize(width: 0, height: 40)

        let collectionView = UICollectionView(frame: .zero, collectionViewLayout: collectionViewFlowLayout)
        view.addSubview(collectionView)
        collectionView.translatesAutoresizingMaskIntoConstraints = false
        collectionView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
        collectionView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
        collectionView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor).isActive = true
        collectionView.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor).isActive = true

        collectionView.register(CollectionViewCell.self, forCellWithReuseIdentifier: cellIdentifier)
        collectionView.register(SupplementaryView.self,
                                forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader,
                                withReuseIdentifier: supplementaryViewIdentifier)

        collectionView.backgroundColor = .white
        self.collectionView = collectionView
        collectionView.delegate = self
        showAllPhotosButtonTouchedInside()

        navigationItem.leftBarButtonItem = UIBarButtonItem(title: "All", style: .done, target: self,
                                                           action: #selector(showAllPhotosButtonTouchedInside))
        navigationItem.rightBarButtonItem = UIBarButtonItem(title: "last 3", style: .done, target: self,
                                                            action: #selector(showLastSeveralPhotosButtonTouchedInside))
    }

    @objc func showAllPhotosButtonTouchedInside() {
    photoLibrary.getAllAlbums { [weak self] albums in
        self?.set(albums: albums)
        if self?.collectionView?.dataSource == nil {
            self?.collectionView?.dataSource = self
        } else {
            self?.collectionView?.reloadData()
        }
    }
    }

    @objc func showLastSeveralPhotosButtonTouchedInside() {
        photoLibrary.getAllAlbums { [weak self] albums in
            guard let firstAlbum = albums.first else { return }
            var album: PhotoAlbumViewModel!
            let maxPhotosToDisplay = 3
            switch firstAlbum {
                case .allPhotos(let id, let title, let count, let image, let isSelected):
                    let newCount = count > maxPhotosToDisplay ? maxPhotosToDisplay : count
                    album = .allPhotos(id: id, title: title, count: newCount, image: image, isSelected: isSelected)
                case .regular(let id, let title, let count, let image, let isSelected):
                    let newCount = count > maxPhotosToDisplay ? maxPhotosToDisplay : count
                    album = .regular(id: id, title: title, count: newCount, image: image, isSelected: isSelected)
            }
            self?.set(albums: [album])
            if self?.collectionView?.dataSource == nil {
                self?.collectionView?.dataSource = self
            } else {
                self?.collectionView?.reloadData()
            }
        }
    }

    private func set(albums: [PhotoAlbumViewModel]) {
        self.albums = albums
        var counter = 0
        for (section, album) in albums.enumerated() {
            for row in 0..<album.count {
                self.cellsTags[IndexPath(row: row, section: section)] = counter
                counter += 1
            }
        }
    }
}

extension ViewController: UICollectionViewDataSource {

    func numberOfSections(in collectionView: UICollectionView) -> Int { return albums.count }
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return albums[section].count
    }

    func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
        let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: supplementaryViewIdentifier, for: indexPath) as! SupplementaryView
        header.label?.text = albums[indexPath.section].title
        return header
    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellIdentifier, for: indexPath) as! CollectionViewCell
        let tag = cellsTags[indexPath]!
        cell.tag = tag
        photoLibrary.getImage(albumId: albums[indexPath.section].id,
                              index: indexPath.item, userInfo: [tagKey: tag],
                              targetSize: thumbnailImageSize,
                              deliveryMode: .fastFormat,
                              resizeMode: .fast) { [weak self, weak cell] (image, userInfo) in
                                guard   let cell = cell, let tagKey = self?.tagKey,
                                        let cellTag = userInfo?[tagKey] as? Int,
                                        cellTag == cell.tag else { return }
                                cell.imageView?.image = image
        }
        return cell
    }
}

extension ViewController: UICollectionViewDelegate {}

CollectionViewCell.swift

import UIKit

class CollectionViewCell: UICollectionViewCell {
    weak var imageView: UIImageView?

    override init(frame: CGRect) {
        super.init(frame: frame)
        clipsToBounds = true
        let imageView = UIImageView(frame: .zero)
        imageView.contentMode = .scaleAspectFill
        addSubview(imageView)
        imageView.translatesAutoresizingMaskIntoConstraints = false
        imageView.topAnchor.constraint(equalTo: topAnchor).isActive = true
        imageView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
        imageView.leftAnchor.constraint(equalTo: leftAnchor).isActive = true
        imageView.rightAnchor.constraint(equalTo: rightAnchor).isActive = true
        self.imageView = imageView
        backgroundColor = UIColor.lightGray.withAlphaComponent(0.3)
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }

    override func prepareForReuse() {
        super.prepareForReuse()
        imageView?.image = nil
    }
}

SupplementaryView.swift

import UIKit

class SupplementaryView: UICollectionReusableView {

    weak var label: UILabel?
    override init(frame: CGRect) {
        super.init(frame: frame)
        backgroundColor = .white
        let label = UILabel(frame: frame)
        label.textColor = .black
        addSubview(label)
        self.label = label
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }

    override func prepareForReuse() {
        super.prepareForReuse()
        self.label?.text = nil
    }
}

Storyboard

enter image description here

Results

enter image description here enter image description here

Breach answered 31/3, 2017 at 13:40 Comment(7)
Great stuff. But why is this: self.photoLibrary.setPhoto(at: indexPath.row) { image in if let image = image { DispatchQueue.main.async { imageView.image = image } } } so slow? Maybe only in simulator?Waft
This is small sample. You need to organise your own image loading stack.Breach
It was slow only in simulator! :)Waft
@VasilyBodnarchuk can you add structure for AtomicArray ?Eolande
@PratikSodha thank you! I forgot to add link to the AtomicArray. It is here: https://mcmap.net/q/15626/-create-thread-safe-array-in-swift (also, I updated the answer)Breach
@VasilyBodnarchuk Thank you.. Good answer. Create and share demo for same.Eolande
@VasilyBodnarchuk This code doesnt output anything if user chooses different permission such as "Allow Selected Photos". Crashes on PhotoKit for ios14Bernhard
G
15

Here's an elegant solution with efficiency in Swift 4.

In short, we request the latest photo assets once, then convert them into image when needed.

First import Photos Library:

import Photos

Then create a function to fetch the lastest photos taken:

func fetchLatestPhotos(forCount count: Int?) -> PHFetchResult<PHAsset> {

    // Create fetch options.
    let options = PHFetchOptions()

    // If count limit is specified.
    if let count = count { options.fetchLimit = count }

    // Add sortDescriptor so the lastest photos will be returned.
    let sortDescriptor = NSSortDescriptor(key: "creationDate", ascending: false)
    options.sortDescriptors = [sortDescriptor]

    // Fetch the photos.
    return PHAsset.fetchAssets(with: .image, options: options)

}

In your case you might want to fetch enough photos at once (for example 50), then store the result somewhere in your view controller:

var latestPhotoAssetsFetched: PHFetchResult<PHAsset>? = nil

In viewDidLoad:

self.latestPhotoAssetsFetched = self.fetchLatestPhotos(forCount: 50)

Finally request the image at the right place (for example, a collection view cell):

override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {

    /*
     ...your code to configure the cell...
     */

    // Get the asset. If nothing, return the cell.
    guard let asset = self.latestPhotoAssetsFetched?[indexPath.item] else {
        return cell
    }
    // Here we bind the asset with the cell.
    cell.representedAssetIdentifier = asset.localIdentifier
    // Request the image.
    PHImageManager.default().requestImage(for: asset,
                                   targetSize: cell.imageView.frame.size,
                                  contentMode: .aspectFill,
                                      options: nil) { (image, _) in
        // By the time the image is returned, the cell may has been recycled.
        // We update the UI only when it is still on the screen.
        if cell.representedAssetIdentifier == asset.localIdentifier {
            cell.imageView.image = image
        }
    }
    return cell
}

Remember to add a property to your cell:

class PhotoCell: UICollectionViewCell {
    var representedAssetIdentifier: String? = nil
}
Glorify answered 13/1, 2018 at 9:1 Comment(1)
How to also fetch videos at the same time?Reception
L
6

You can extract the 3 latest photos using functions in the AssetsLibrary framework. First you have to add the framework to the project. The following function retrieves the 3 latest photos and calls the completion block.

import AssetsLibrary

func getLatestPhotos(completion completionBlock : ([UIImage] -> ()))   {
    let library = ALAssetsLibrary()
    var count = 0
    var images : [UIImage] = []
    var stopped = false

    library.enumerateGroupsWithTypes(ALAssetsGroupSavedPhotos, usingBlock: { (group,var stop) -> Void in

        group?.setAssetsFilter(ALAssetsFilter.allPhotos())

        group?.enumerateAssetsWithOptions(NSEnumerationOptions.Reverse, usingBlock: {
            (asset : ALAsset!, index, var stopEnumeration) -> Void in

            if (!stopped)
            {
                if count >= 3
                {

                    stopEnumeration.memory = ObjCBool(true)
                    stop.memory = ObjCBool(true)
                    completionBlock(images)
                    stopped = true
                }
                else
                {
                    // For just the thumbnails use the following line.
                    let cgImage = asset.thumbnail().takeUnretainedValue()

                    // Use the following line for the full image.
                    let cgImage = asset.defaultRepresentation().fullScreenImage().takeUnretainedValue()

                    if let image = UIImage(CGImage: cgImage) {
                        images.append(image)
                        count += 1
                    }
                }
            }

        })

        },failureBlock : { error in
            println(error)
    })
}

The above function can be called like this

getLatestPhotos(completion: { images in
     println(images)
     //Set Images in this block.
})
Livvie answered 1/2, 2015 at 7:18 Comment(4)
Is there away to look at photos by date? Looking into that one now. Meta data from the library I'm assuming is accessible.Contrariwise
fetchOptions.sortDescriptors = [NSSortDescriptor(key:"creationDate", ascending: true)]Contrariwise
Outdated for swift 2, can you update please? @LivvieAegean
IMPORTANT The Assets Library framework is deprecated as of iOS 9.0. Instead, use the Photos framework instead, which in iOS 8.0 and later provides more features and better performance for working with a user’s photo library. For more information, see Photos Framework Reference.Jayjaycee
G
6

Here is @Lindsey Scott's answer but in Objective-C. I am putting the last 9 photos from Camera Roll into a collection view:

-(void)fetchPhotoFromEndAtIndex:(int)index{

PHImageRequestOptions *options = [[PHImageRequestOptions alloc]init];
options.synchronous = YES;
PHFetchOptions *fetchOptions = [[PHFetchOptions alloc]init];
fetchOptions.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:YES]];

PHFetchResult *photos = [PHAsset fetchAssetsWithMediaType:PHAssetMediaTypeImage options:fetchOptions];
if (photos) {
    [[PHImageManager defaultManager] requestImageForAsset:[photos objectAtIndex:photos.count -1 -index] targetSize:CGSizeMake(self.collectionView.frame.size.width/3, self.collectionView.frame.size.height/3) contentMode:PHImageContentModeAspectFill options:options resultHandler:^(UIImage *result, NSDictionary *info) {
        [self.imagesArray addObject:result];

        if (index + 1 < photos.count && self.imagesArray.count < 9) {
            [self fetchPhotoFromEndAtIndex:index + 1];
        }
    }];
}

[self.collectionView reloadData];

}
Greerson answered 1/7, 2015 at 17:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.