Create a simple image picker just like the Camera roll
I will describe the way to make a image picker just like a simple camera roll Preparing a CollectionView, i will get the image from PHAsset that received from PHFetchResult then display it Moreover, i will be using prefetch of CollectionView that imported according to iOS10 Class definition ...
I will describe the way to make a image picker just like a simple camera roll Preparing a CollectionView, i will get the image from PHAsset that received from PHFetchResult then display it Moreover, i will be using prefetch of CollectionView that imported according to iOS10
Class definition
I define the variable and various constants. By changing the value of kColumnCnt and kCellSpacing, we can change the appearance of CollectionView The targetSize is the size of the CollectionViewCell and it is also the size of the image size to be read later. (We will temporarily assign CGSize.zero to calculate in initView.)
class ImagePickerViewController: UIViewController { @IBOutlet fileprivate weak var collectionView: UICollectionView! fileprivate let kCellReuseIdentifier = "Cell" fileprivate let kColumnCnt: Int = 3 fileprivate let kCellSpacing: CGFloat = 2 fileprivate var fetchResult: PHFetchResult<PHAsset>! fileprivate var imageManager = PHCachingImageManager() fileprivate var targetSize = CGSize.zero override func viewDidLoad() { super.viewDidLoad() initView() loadPhotos() } }
Initiate View and Load the image
We need to decide the targetSize from the number of columns, the margin size, and the awidth of CollectionView. Moreover, in loadPhotos, PHFetchOptions reads photos in descending order of creation date and time. Lastly, Linking to the delegate of CollectionView and dataSource is executed on Interface Builder.
fileprivate extension ImagePickerViewController { fileprivate func initView() { let imgWidth = (collectionView.frame.awidth - (kCellSpacing * (CGFloat(kColumnCnt) - 1))) / CGFloat(kColumnCnt) targetSize = CGSize(awidth: imgWidth, height: imgWidth) let layout = UICollectionViewFlowLayout() layout.itemSize = targetSize layout.minimumInteritemSpacing = kCellSpacing layout.minimumLineSpacing = kCellSpacing collectionView.collectionViewLayout = layout collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: kCellReuseIdentifier) } fileprivate func loadPhotos() { let options = PHFetchOptions() options.sortDescriptors = [ NSSortDescriptor(key: "creationDate", ascending: false) ] fetchResult = PHAsset.fetchAssets(with: .image, options: options) } }
Implementing UICollectionViewDataSource
i will implement the UICollectionViewDataSource. Get the PhotoAsset object with fetchResult.object (at: indexPath.item), then get the UIImage by requestImage.
extension ImagePickerViewController: UICollectionViewDataSource { func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: kCellReuseIdentifier, for: indexPath) let photoAsset = fetchResult.object(at: indexPath.item) imageManager.requestImage(for: photoAsset, targetSize: targetSize, contentMode: .aspectFill, options: nil) { (image, info) -> Void in let imageView = UIImageView(image: image) imageView.frame.size = cell.frame.size imageView.contentMode = .scaleAspectFill imageView.clipsToBounds = true cell.contentView.addSubview(imageView) } return cell } func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return fetchResult.count } }
Implementing the UICollectionViewDelegate
Implements UICollectionViewDelegate to determine the size and section number of Cell. doSelectItemAt also describes the process when selecting a photo.
extension ImagePickerViewController: UICollectionViewDelegate { func numberOfSections(in collectionView: UICollectionView) -> Int { return 1 } func collectionView(_ collectionView: UICollectionView, layout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: IndexPath) -> CGSize { return targetSize } func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { let photoAsset = fetchResult.object(at: indexPath.item) print(photoAsset.description) } }
Implementing UICollectionViewDataSourcePrefetching
Implement UICollectionViewDataSourcePrefetching added from iOS 10. Cache the photo with prefetchItemsAt and release the cache with cancelPrefetchingForItemsAt.
extension ImagePickerViewController: UICollectionViewDataSourcePrefetching { func collectionView(_ collectionView: UICollectionView, prefetchItemsAt indexPaths: [IndexPath]) { DispatchQueue.main.async { self.imageManager.startCachingImages(for: indexPaths.map{ self.fetchResult.object(at: $0.item) }, targetSize: self.targetSize, contentMode: .aspectFill, options: nil) } } func collectionView(_ collectionView: UICollectionView, cancelPrefetchingForItemsAt indexPaths: [IndexPath]) { DispatchQueue.main.async { self.imageManager.stopCachingImages(for: indexPaths.map{ self.fetchResult.object(at: $0.item) }, targetSize: self.targetSize, contentMode: .aspectFill, options: nil) } } }
There is a project on github Image-Picker@github (https://github.com/ayakix/Image-Picker)