Switch asset picker to use diffable data sources

This commit is contained in:
Shadowfacts 2020-07-06 18:16:18 -04:00
parent 985d30a401
commit fe09c5e522
Signed by: shadowfacts
GPG Key ID: 94A5AB95422746E5
1 changed files with 63 additions and 56 deletions

View File

@ -22,20 +22,22 @@ class AssetCollectionViewController: UICollectionViewController {
weak var delegate: AssetCollectionViewControllerDelegate?
var flowLayout: UICollectionViewFlowLayout {
private var dataSource: UICollectionViewDiffableDataSource<Section, Item>!
private var flowLayout: UICollectionViewFlowLayout {
return collectionViewLayout as! UICollectionViewFlowLayout
}
var availableWidth: CGFloat!
var thumbnailSize: CGSize!
private var availableWidth: CGFloat!
private var thumbnailSize: CGSize!
let imageManager = PHCachingImageManager()
var fetchResult: PHFetchResult<PHAsset>!
private let imageManager = PHCachingImageManager()
private var fetchResult: PHFetchResult<PHAsset>!
var selectedAssets: [PHAsset] {
return collectionView.indexPathsForSelectedItems?.map({ (indexPath) in
fetchResult.object(at: indexPath.row - 1)
}) ?? []
return collectionView.indexPathsForSelectedItems?.compactMap { (indexPath) in
guard case let .asset(asset) = dataSource.itemIdentifier(for: indexPath) else { return nil }
return asset
} ?? []
}
init() {
@ -71,14 +73,42 @@ class AssetCollectionViewController: UICollectionViewController {
collectionView.register(UINib(nibName: "AssetCollectionViewCell", bundle: .main), forCellWithReuseIdentifier: reuseIdentifier)
collectionView.register(UINib(nibName: "ShowCameraCollectionViewCell", bundle: .main), forCellWithReuseIdentifier: cameraReuseIdentifier)
dataSource = UICollectionViewDiffableDataSource<Section, Item>(collectionView: collectionView, cellProvider: { (collectionView, indexPath, item) -> UICollectionViewCell? in
switch item {
case .showCamera:
return collectionView.dequeueReusableCell(withReuseIdentifier: cameraReuseIdentifier, for: indexPath)
case let .asset(asset):
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! AssetCollectionViewCell
cell.updateUI(asset: asset)
self.imageManager.requestImage(for: asset, targetSize: self.thumbnailSize, contentMode: .aspectFill, options: nil) { (image, _) in
guard let image = image else { return }
DispatchQueue.main.async {
guard cell.assetIdentifier == asset.localIdentifier else { return }
cell.thumbnailImage = image
}
}
return cell
}
})
let options = PHFetchOptions()
options.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
fetchResult = fetchAssets(with: options)
var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
snapshot.appendSections([.assets])
var items: [Item] = [.showCamera]
fetchResult.enumerateObjects { (asset, _, _) in
items.append(.asset(asset))
}
snapshot.appendItems(items)
dataSource.apply(snapshot, animatingDifferences: false)
collectionView.allowsMultipleSelection = true
setEditing(true, animated: false)
updateItemsSelected()
updateItemsSelectedCount()
if let singleFingerPanGesture = collectionView.gestureRecognizers?.first(where: {
$0.name == "multi-select.singleFingerPanGesture"
@ -115,43 +145,12 @@ class AssetCollectionViewController: UICollectionViewController {
return PHAsset.fetchAssets(with: options)
}
func updateItemsSelected() {
func updateItemsSelectedCount() {
let selected = collectionView.indexPathsForSelectedItems?.count ?? 0
navigationItem.title = "\(selected) selected"
}
// MARK: UICollectionViewDataSource
override func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return fetchResult.count + 1
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if indexPath.row == 0 {
return collectionView.dequeueReusableCell(withReuseIdentifier: cameraReuseIdentifier, for: indexPath)
} else {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! AssetCollectionViewCell
let asset = fetchResult.object(at: indexPath.row - 1)
cell.updateUI(asset: asset)
imageManager.requestImage(for: asset, targetSize: thumbnailSize, contentMode: .aspectFill, options: nil) { (image, _) in
guard let image = image else { return }
DispatchQueue.main.async {
guard cell.assetIdentifier == asset.localIdentifier else { return }
cell.thumbnailImage = image
}
}
return cell
}
}
// MARK: UICollectionViewDelegate
override func collectionView(_ collectionView: UICollectionView, shouldBeginMultipleSelectionInteractionAt indexPath: IndexPath) -> Bool {
@ -159,36 +158,34 @@ class AssetCollectionViewController: UICollectionViewController {
}
override func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool {
if indexPath.row > 0,
let delegate = delegate {
let asset = fetchResult.object(at: indexPath.row - 1)
guard let item = dataSource.itemIdentifier(for: indexPath) else { return false }
if let delegate = delegate,
case let .asset(asset) = item {
return delegate.shouldSelectAsset(asset)
}
return true
}
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if indexPath.row == 0 {
guard let item = dataSource.itemIdentifier(for: indexPath) else { return }
switch item {
case .showCamera:
collectionView.deselectItem(at: indexPath, animated: false)
delegate?.captureFromCamera()
} else {
updateItemsSelected()
case .asset(_):
updateItemsSelectedCount()
}
}
override func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
updateItemsSelected()
updateItemsSelectedCount()
}
override func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? {
if indexPath.row == 0 {
return nil
} else {
let asset = fetchResult.object(at: indexPath.row - 1)
return UIContextMenuConfiguration(identifier: indexPath as NSIndexPath, previewProvider: { () -> UIViewController? in
return AssetPreviewViewController(asset: asset)
}, actionProvider: nil)
}
guard case let .asset(asset) = dataSource.itemIdentifier(for: indexPath) else { return nil }
return UIContextMenuConfiguration(identifier: indexPath as NSIndexPath, previewProvider: { () -> UIViewController? in
return AssetPreviewViewController(asset: asset)
}, actionProvider: nil)
}
override func collectionView(_ collectionView: UICollectionView, previewForHighlightingContextMenuWithConfiguration configuration: UIContextMenuConfiguration) -> UITargetedPreview? {
@ -210,3 +207,13 @@ class AssetCollectionViewController: UICollectionViewController {
}
}
extension AssetCollectionViewController {
enum Section: Hashable {
case assets
}
enum Item: Hashable {
case showCamera
case asset(PHAsset)
}
}