import UIKit
import Photos
import ImageIO
class ViewController: UIViewController {
@IBOutlet weak var safeAreaView: UIView!
@IBOutlet weak var collectionView: UICollectionView!
private let fetchQueue = DispatchQueue(label: "FetchQueue")
private var fetchAssets = PHFetchResult<PHAsset>()
private var albumTitle: String?
override func viewDidLoad() {
super.viewDidLoad()
requestAuthorization()
}
private func requestAuthorization() {
PHPhotoLibrary.requestAuthorization { status in
DispatchQueue.main.async {
switch status {
case .authorized: self.fetchAssetCollections()
default: print("ERROR: RequestAuthorization")
}
}
}
}
private func fetchAssetCollections() {
if let album = PHAssetCollection.fetchAssetCollections(with: .smartAlbum, subtype: .smartAlbumUserLibrary, options: nil).firstObject {
albumTitle = album.localizedTitle
fetchAssets = PHAsset.fetchAssets(in: album, options: nil)
collectionView.reloadData()
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
PHPhotoLibrary.shared().register(self)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
PHPhotoLibrary.shared().unregisterChangeObserver(self)
}
}
extension ViewController: PHPhotoLibraryChangeObserver {
func photoLibraryDidChange(_ changeInstance: PHChange) {
guard let isChanged = changeInstance.changeDetails(for: fetchAssets) else {return}
fetchAssets = isChanged.fetchResultAfterChanges
DispatchQueue.main.async {
guard isChanged.hasIncrementalChanges else {
self.collectionView.reloadData()
return
}
self.collectionView.performBatchUpdates({
if let insert = isChanged.insertedIndexes, !insert.isEmpty {
let indexPath = insert.map {IndexPath(item: $0, section: 0)}
self.collectionView.insertItems(at: indexPath)
}
if let change = isChanged.changedIndexes, !change.isEmpty {
let indexPath = change.map {IndexPath(item: $0, section: 0)}
self.collectionView.reloadItems(at: indexPath)
}
if let remove = isChanged.removedIndexes, !remove.isEmpty {
let indexPath = remove.map {IndexPath(item: $0, section: 0)}
self.collectionView.deleteItems(at: indexPath)
}
isChanged.enumerateMoves { (at, to) in
let atIP = IndexPath(item: at, section: 0)
let toIP = IndexPath(item: to, section: 0)
self.collectionView.moveItem(at: atIP, to: toIP)
}
})
}
}
}
extension ViewController: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return fetchAssets.count
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
guard let flowLayout = collectionViewLayout as? UICollectionViewFlowLayout else {
return collectionView.bounds.size
}
let cellCount: CGFloat = 4
let minimumInteritemSpacing = flowLayout.minimumInteritemSpacing * (cellCount - 1)
let size = floor((collectionView.bounds.width - minimumInteritemSpacing) / cellCount)
return CGSize(width: size, height: size)
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionViewCell", for: indexPath) as! CollectionViewCell
fetchQueue.async {
self.thumbnail(using: indexPath) {cell.imageView.image = $0}
}
return cell
}
private func thumbnail(using indexPath: IndexPath, _ completion: ((UIImage?) -> ())?) {
let asset = self.fetchAssets[indexPath.row]
asset.requestContentEditingInput(with: nil) { (input, info) in
guard let url = input?.fullSizeImageURL else {
completion?(nil)
return
}
guard let imageSource = CGImageSourceCreateWithURL(url as CFURL, nil) else {
completion?(nil)
return
}
let options: [CFString : Any] = [kCGImageSourceThumbnailMaxPixelSize: 125,
kCGImageSourceCreateThumbnailFromImageAlways: true]
guard let scaledImage = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, options as CFDictionary) else {
completion?(nil)
return
}
let orientation = UInt32(input?.fullSizeImageOrientation ?? 1)
guard let cgOrientation = CGImagePropertyOrientation(rawValue: orientation) else {
completion?(nil)
return
}
let uiOrientation = UIImageOrientation(cgOrientation)
let uiImage = UIImage(cgImage: scaledImage, scale: 1, orientation: uiOrientation)
completion?(uiImage)
}
}
}
class CollectionViewCell: UICollectionViewCell {
@IBOutlet weak var imageView: UIImageView!
}
extension UIImageOrientation {
init(_ cgOrientation: CGImagePropertyOrientation) {
switch cgOrientation {
case .up: self = .up
case .upMirrored: self = .upMirrored
case .down: self = .down
case .downMirrored: self = .downMirrored
case .left: self = .left
case .leftMirrored: self = .leftMirrored
case .right: self = .right
case .rightMirrored: self = .rightMirrored
}
}
}