Create a simple image picker just like the Camera roll
Bài đăng này đã không được cập nhật trong 3 năm
Overview
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
What is ImagePickerViewController
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 width 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.width - (kCellSpacing * (CGFloat(kColumnCnt) - 1))) / CGFloat(kColumnCnt)
targetSize = CGSize(width: 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)
}
}
}
Sample
There is a project on github Image-Picker@github (https://github.com/ayakix/Image-Picker)
All rights reserved