Tusker/Tusker/Screens/Compose/Asset Picker/AssetCollectionViewControll...

187 lines
6.6 KiB
Swift

//
// AssetCollectionViewController.swift
// Tusker
//
// Created by Shadowfacts on 1/1/20.
// Copyright © 2020 Shadowfacts. All rights reserved.
//
import UIKit
import Photos
private let reuseIdentifier = "assetCell"
private let cameraReuseIdentifier = "showCameraCell"
protocol AssetCollectionViewControllerDelegate: class {
func shouldSelectAsset(_ asset: PHAsset) -> Bool
func didSelectAssets(_ assets: [PHAsset])
func captureFromCamera()
}
class AssetCollectionViewController: UICollectionViewController {
weak var delegate: AssetCollectionViewControllerDelegate?
var flowLayout: UICollectionViewFlowLayout {
return collectionViewLayout as! UICollectionViewFlowLayout
}
var availableWidth: CGFloat!
var thumbnailSize: CGSize!
let imageManager = PHCachingImageManager()
var fetchResult: PHFetchResult<PHAsset>!
var selectedAssets: [PHAsset] {
return collectionView.indexPathsForSelectedItems?.map({ (indexPath) in
fetchResult.object(at: indexPath.row - 1)
}) ?? []
}
init() {
super.init(collectionViewLayout: UICollectionViewFlowLayout())
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(donePressed))
collectionView.alwaysBounceVertical = true
collectionView.register(UINib(nibName: "AssetCollectionViewCell", bundle: .main), forCellWithReuseIdentifier: reuseIdentifier)
collectionView.register(UINib(nibName: "ShowCameraCollectionViewCell", bundle: .main), forCellWithReuseIdentifier: cameraReuseIdentifier)
let options = PHFetchOptions()
options.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
fetchResult = fetchAssets(with: options)
collectionView.allowsMultipleSelection = true
setEditing(true, animated: false)
updateItemsSelected()
if let singleFingerPanGesture = collectionView.gestureRecognizers?.first(where: {
$0.name == "multi-select.singleFingerPanGesture"
}),
let interactivePopGesture = navigationController?.interactivePopGestureRecognizer {
singleFingerPanGesture.require(toFail: interactivePopGesture)
}
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
let availableWidth = view.bounds.inset(by: view.safeAreaInsets).width
if self.availableWidth != availableWidth {
self.availableWidth = availableWidth
let size = (availableWidth - 8) / 3
flowLayout.itemSize = CGSize(width: size, height: size)
flowLayout.minimumInteritemSpacing = 4
flowLayout.minimumLineSpacing = 4
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
let scale = UIScreen.main.scale
let cellSize = flowLayout.itemSize
thumbnailSize = CGSize(width: cellSize.width * scale, height: cellSize.height * scale)
}
open func fetchAssets(with options: PHFetchOptions) -> PHFetchResult<PHAsset> {
return PHAsset.fetchAssets(with: options)
}
func updateItemsSelected() {
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 {
return true
}
override func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool {
if indexPath.row > 0,
let delegate = delegate {
let asset = fetchResult.object(at: indexPath.row - 1)
return delegate.shouldSelectAsset(asset)
}
return true
}
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if indexPath.row == 0 {
collectionView.deselectItem(at: indexPath, animated: false)
delegate?.captureFromCamera()
} else {
updateItemsSelected()
}
}
override func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
updateItemsSelected()
}
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: nil, previewProvider: { () -> UIViewController? in
return AssetPreviewViewController(asset: asset)
}, actionProvider: nil)
}
}
// MARK: - Interaction
@objc func donePressed() {
delegate?.didSelectAssets(selectedAssets)
dismiss(animated: true)
}
}