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 untrusted user: 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? weak var delegate: AssetCollectionViewControllerDelegate?
var flowLayout: UICollectionViewFlowLayout { private var dataSource: UICollectionViewDiffableDataSource<Section, Item>!
private var flowLayout: UICollectionViewFlowLayout {
return collectionViewLayout as! UICollectionViewFlowLayout return collectionViewLayout as! UICollectionViewFlowLayout
} }
var availableWidth: CGFloat! private var availableWidth: CGFloat!
var thumbnailSize: CGSize! private var thumbnailSize: CGSize!
let imageManager = PHCachingImageManager() private let imageManager = PHCachingImageManager()
var fetchResult: PHFetchResult<PHAsset>! private var fetchResult: PHFetchResult<PHAsset>!
var selectedAssets: [PHAsset] { var selectedAssets: [PHAsset] {
return collectionView.indexPathsForSelectedItems?.map({ (indexPath) in return collectionView.indexPathsForSelectedItems?.compactMap { (indexPath) in
fetchResult.object(at: indexPath.row - 1) guard case let .asset(asset) = dataSource.itemIdentifier(for: indexPath) else { return nil }
}) ?? [] return asset
} ?? []
} }
init() { init() {
@ -71,14 +73,42 @@ class AssetCollectionViewController: UICollectionViewController {
collectionView.register(UINib(nibName: "AssetCollectionViewCell", bundle: .main), forCellWithReuseIdentifier: reuseIdentifier) collectionView.register(UINib(nibName: "AssetCollectionViewCell", bundle: .main), forCellWithReuseIdentifier: reuseIdentifier)
collectionView.register(UINib(nibName: "ShowCameraCollectionViewCell", bundle: .main), forCellWithReuseIdentifier: cameraReuseIdentifier) 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() let options = PHFetchOptions()
options.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)] options.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
fetchResult = fetchAssets(with: options) 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 collectionView.allowsMultipleSelection = true
setEditing(true, animated: false) setEditing(true, animated: false)
updateItemsSelected() updateItemsSelectedCount()
if let singleFingerPanGesture = collectionView.gestureRecognizers?.first(where: { if let singleFingerPanGesture = collectionView.gestureRecognizers?.first(where: {
$0.name == "multi-select.singleFingerPanGesture" $0.name == "multi-select.singleFingerPanGesture"
@ -115,43 +145,12 @@ class AssetCollectionViewController: UICollectionViewController {
return PHAsset.fetchAssets(with: options) return PHAsset.fetchAssets(with: options)
} }
func updateItemsSelected() { func updateItemsSelectedCount() {
let selected = collectionView.indexPathsForSelectedItems?.count ?? 0 let selected = collectionView.indexPathsForSelectedItems?.count ?? 0
navigationItem.title = "\(selected) selected" 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 // MARK: UICollectionViewDelegate
override func collectionView(_ collectionView: UICollectionView, shouldBeginMultipleSelectionInteractionAt indexPath: IndexPath) -> Bool { override func collectionView(_ collectionView: UICollectionView, shouldBeginMultipleSelectionInteractionAt indexPath: IndexPath) -> Bool {
@ -159,37 +158,35 @@ class AssetCollectionViewController: UICollectionViewController {
} }
override func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool { override func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool {
if indexPath.row > 0, guard let item = dataSource.itemIdentifier(for: indexPath) else { return false }
let delegate = delegate { if let delegate = delegate,
let asset = fetchResult.object(at: indexPath.row - 1) case let .asset(asset) = item {
return delegate.shouldSelectAsset(asset) return delegate.shouldSelectAsset(asset)
} }
return true return true
} }
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { 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) collectionView.deselectItem(at: indexPath, animated: false)
delegate?.captureFromCamera() delegate?.captureFromCamera()
} else { case .asset(_):
updateItemsSelected() updateItemsSelectedCount()
} }
} }
override func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) { override func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
updateItemsSelected() updateItemsSelectedCount()
} }
override func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? { override func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? {
if indexPath.row == 0 { guard case let .asset(asset) = dataSource.itemIdentifier(for: indexPath) else { return nil }
return nil
} else {
let asset = fetchResult.object(at: indexPath.row - 1)
return UIContextMenuConfiguration(identifier: indexPath as NSIndexPath, previewProvider: { () -> UIViewController? in return UIContextMenuConfiguration(identifier: indexPath as NSIndexPath, previewProvider: { () -> UIViewController? in
return AssetPreviewViewController(asset: asset) return AssetPreviewViewController(asset: asset)
}, actionProvider: nil) }, actionProvider: nil)
} }
}
override func collectionView(_ collectionView: UICollectionView, previewForHighlightingContextMenuWithConfiguration configuration: UIContextMenuConfiguration) -> UITargetedPreview? { override func collectionView(_ collectionView: UICollectionView, previewForHighlightingContextMenuWithConfiguration configuration: UIContextMenuConfiguration) -> UITargetedPreview? {
if let indexPath = (configuration.identifier as? NSIndexPath) as IndexPath?, if let indexPath = (configuration.identifier as? NSIndexPath) as IndexPath?,
@ -210,3 +207,13 @@ class AssetCollectionViewController: UICollectionViewController {
} }
} }
extension AssetCollectionViewController {
enum Section: Hashable {
case assets
}
enum Item: Hashable {
case showCamera
case asset(PHAsset)
}
}