From fe09c5e522558e5fc920c10d4c1bbf293038d8bf Mon Sep 17 00:00:00 2001 From: Shadowfacts Date: Mon, 6 Jul 2020 18:16:18 -0400 Subject: [PATCH] Switch asset picker to use diffable data sources --- .../AssetCollectionViewController.swift | 119 +++++++++--------- 1 file changed, 63 insertions(+), 56 deletions(-) diff --git a/Tusker/Screens/Asset Picker/AssetCollectionViewController.swift b/Tusker/Screens/Asset Picker/AssetCollectionViewController.swift index 1d584d14..8d883902 100644 --- a/Tusker/Screens/Asset Picker/AssetCollectionViewController.swift +++ b/Tusker/Screens/Asset Picker/AssetCollectionViewController.swift @@ -22,20 +22,22 @@ class AssetCollectionViewController: UICollectionViewController { weak var delegate: AssetCollectionViewControllerDelegate? - var flowLayout: UICollectionViewFlowLayout { + private var dataSource: UICollectionViewDiffableDataSource! + 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! + private let imageManager = PHCachingImageManager() + private var fetchResult: PHFetchResult! 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(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() + 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) + } +}