Skip to content

Commit

Permalink
Merge pull request #70 from gezihuzi/master
Browse files Browse the repository at this point in the history
Added support for asynchronous asset loading
  • Loading branch information
DragonCherry authored Apr 23, 2020
2 parents b8c2eab + 1432d26 commit a1b4d10
Show file tree
Hide file tree
Showing 3 changed files with 184 additions and 73 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,18 @@ open class AssetsAlbumViewController: UIViewController {
return view
}()

fileprivate lazy var loadingActivityIndicatorView: UIActivityIndicatorView = {

if #available(iOS 13.0, *) {
let indicator = UIActivityIndicatorView(style: .large)
return indicator
} else {
let indicator = UIActivityIndicatorView()
return indicator
}
}()
fileprivate lazy var loadingPlaceholderView: UIView = UIView()

public required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
Expand All @@ -84,6 +96,8 @@ open class AssetsAlbumViewController: UIViewController {
view.backgroundColor = .ap_background

view.addSubview(collectionView)
view.addSubview(loadingPlaceholderView)
view.addSubview(loadingActivityIndicatorView)
view.setNeedsUpdateConstraints()

AssetsManager.shared.subscribe(subscriber: self)
Expand All @@ -102,10 +116,24 @@ open class AssetsAlbumViewController: UIViewController {
make.edges.equalToSuperview()
}

loadingPlaceholderView.isHidden = true
loadingPlaceholderView.backgroundColor = .white
loadingPlaceholderView.snp.makeConstraints { (make) in
make.edges.equalToSuperview()
}

loadingActivityIndicatorView.snp.makeConstraints { (make) in
make.center.equalToSuperview()
}

AssetsManager.shared.authorize(completion: { [weak self] isAuthorized in
if isAuthorized {
self?.loadingPlaceholderView.isHidden = false
self?.loadingActivityIndicatorView.startAnimating()
AssetsManager.shared.fetchAlbums { (_) in
self?.collectionView.reloadData()
self?.loadingPlaceholderView.isHidden = true
self?.loadingActivityIndicatorView.stopAnimating()
}
} else {
self?.dismiss(animated: true, completion: nil)
Expand Down
92 changes: 62 additions & 30 deletions AssetsPickerViewController/Classes/Assets/AssetsManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ open class AssetsManager: NSObject {
fileprivate(set) open var selectedAlbum: PHAssetCollection?

fileprivate var isFetchedAlbums: Bool = false
fileprivate var resourceLoadingQueue: DispatchQueue = DispatchQueue(label: "com.assetspicker.loader", qos: .userInitiated)

private override init() {
super.init()
Expand Down Expand Up @@ -322,6 +323,25 @@ extension AssetsManager {
return false
}
}

open func selectAsync(album newAlbum: PHAssetCollection, complection: @escaping (Bool) -> Void) {
if let oldAlbumIdentifier = self.selectedAlbum?.localIdentifier, oldAlbumIdentifier == newAlbum.localIdentifier {
logi("Selected same album.")
complection(false)
}
self.selectedAlbum = newAlbum
if let fetchResult = fetchMap[newAlbum.localIdentifier] {
resourceLoadingQueue.async { [weak self] in
let indexSet = IndexSet(0..<fetchResult.count)
self?.assetArray = fetchResult.objects(at: indexSet)
DispatchQueue.main.async {
complection(true)
}
}
} else {
complection(false)
}
}
}

// MARK: - Model Manipulation
Expand Down Expand Up @@ -430,7 +450,7 @@ extension AssetsManager {
// MARK: - Fetch
extension AssetsManager {

open func fetchAlbums(isRefetch: Bool = false, completion: (([[PHAssetCollection]]) -> Void)? = nil) {
open func fetchAlbums(isRefetch: Bool = false, completion: @escaping (([[PHAssetCollection]]) -> Void)) {

if isRefetch {
selectedAlbum = nil
Expand All @@ -442,42 +462,54 @@ extension AssetsManager {
albumMap.removeAll()
}

if !isFetchedAlbums {

let smartAlbumEntry = fetchAlbums(forAlbumType: .smartAlbum)
fetchedAlbumsArray.append(smartAlbumEntry.fetchedAlbums)
sortedAlbumsArray.append(smartAlbumEntry.sortedAlbums)
albumsFetchArray.append(smartAlbumEntry.fetchResult)

let albumEntry = fetchAlbums(forAlbumType: .album)
fetchedAlbumsArray.append(albumEntry.fetchedAlbums)
sortedAlbumsArray.append(albumEntry.sortedAlbums)
albumsFetchArray.append(albumEntry.fetchResult)

if pickerConfig.albumIsShowMomentAlbums {
let momentEntry = fetchAlbums(forAlbumType: .moment)
fetchedAlbumsArray.append(momentEntry.fetchedAlbums)
sortedAlbumsArray.append(momentEntry.sortedAlbums)
albumsFetchArray.append(momentEntry.fetchResult)
resourceLoadingQueue.async { [weak self] in
guard let `self` = self else { return }
if !self.isFetchedAlbums {

let smartAlbumEntry = self.fetchAlbums(forAlbumType: .smartAlbum)
self.fetchedAlbumsArray.append(smartAlbumEntry.fetchedAlbums)
self.sortedAlbumsArray.append(smartAlbumEntry.sortedAlbums)
self.albumsFetchArray.append(smartAlbumEntry.fetchResult)

let albumEntry = self.fetchAlbums(forAlbumType: .album)
self.fetchedAlbumsArray.append(albumEntry.fetchedAlbums)
self.sortedAlbumsArray.append(albumEntry.sortedAlbums)
self.albumsFetchArray.append(albumEntry.fetchResult)

if self.pickerConfig.albumIsShowMomentAlbums {
let momentEntry = self.fetchAlbums(forAlbumType: .moment)
self.fetchedAlbumsArray.append(momentEntry.fetchedAlbums)
self.sortedAlbumsArray.append(momentEntry.sortedAlbums)
self.albumsFetchArray.append(momentEntry.fetchResult)
}
self.isFetchedAlbums = true
}
// notify
DispatchQueue.main.async {
completion(self.sortedAlbumsArray)
}
isFetchedAlbums = true
}
// notify
completion?(sortedAlbumsArray)
}

open func fetchAssets(isRefetch: Bool = false, completion: (([PHAsset]) -> Void)? = nil) {

fetchAlbums(isRefetch: isRefetch)

if isRefetch {
assetArray.removeAll()
}

// set default album
select(album: defaultAlbum ?? cameraRollAlbum)
fetchAlbums(isRefetch: isRefetch, completion: { [weak self] _ in

guard let `self` = self else { return }
if isRefetch {
self.assetArray.removeAll()
}

// set default album
self.selectAsync(album: self.defaultAlbum ?? self.cameraRollAlbum) { [weak self] (result) in
guard let `self` = self else {
completion?([])
return
}
completion?(self.assetArray)
}
})

completion?(assetArray)
}

func fetchAlbums(forAlbumType type: PHAssetCollectionType) -> (fetchedAlbums: [PHAssetCollection], sortedAlbums: [PHAssetCollection], fetchResult: PHFetchResult<PHAssetCollection>) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,18 @@ open class AssetsPhotoViewController: UIViewController {
return view
}()

fileprivate lazy var loadingActivityIndicatorView: UIActivityIndicatorView = {

if #available(iOS 13.0, *) {
let indicator = UIActivityIndicatorView(style: .large)
return indicator
} else {
let indicator = UIActivityIndicatorView()
return indicator
}
}()
fileprivate lazy var loadingPlaceholderView: UIView = UIView()

var selectedAssets: [PHAsset] {
return selectedArray
}
Expand All @@ -111,6 +123,8 @@ open class AssetsPhotoViewController: UIViewController {
view.addSubview(emptyView)
view.addSubview(noPermissionView)
view.setNeedsUpdateConstraints()
view.addSubview(loadingPlaceholderView)
view.addSubview(loadingActivityIndicatorView)
}

override open func viewDidLoad() {
Expand All @@ -119,6 +133,8 @@ open class AssetsPhotoViewController: UIViewController {
setupCommon()
setupBarButtonItems()
setupCollectionView()
setupPlaceholderView()
setupLoadActivityIndicatorView()

updateEmptyView(count: 0)
updateNoPermissionView()
Expand Down Expand Up @@ -270,33 +286,58 @@ extension AssetsPhotoViewController {
}
}

func setupPlaceholderView() {
loadingPlaceholderView.isHidden = true
loadingPlaceholderView.backgroundColor = .white
loadingPlaceholderView.snp.makeConstraints { (make) in
make.edges.equalToSuperview()
}
}

func setupLoadActivityIndicatorView() {
loadingActivityIndicatorView.snp.makeConstraints { (make) in
make.center.equalToSuperview()
}
}

func setupAssets() {
loadingPlaceholderView.isHidden = false
loadingActivityIndicatorView.startAnimating()
let manager = AssetsManager.shared
manager.subscribe(subscriber: self)
manager.fetchAlbums()
manager.fetchAssets() { [weak self] photos in

guard let `self` = self else { return }

self.updateEmptyView(count: photos.count)
self.title = self.title(forAlbum: manager.selectedAlbum)

if self.selectedArray.count > 0 {
self.collectionView.performBatchUpdates({ [weak self] in
self?.collectionView.reloadData()
}, completion: { [weak self] (finished) in
guard let `self` = self else { return }
// initialize preselected assets
self.selectedArray.forEach({ [weak self] (asset) in
if let row = photos.firstIndex(of: asset) {
let indexPathToSelect = IndexPath(row: row, section: 0)
self?.collectionView.selectItem(at: indexPathToSelect, animated: false, scrollPosition: UICollectionView.ScrollPosition(rawValue: 0))
}
})
self.updateSelectionCount()
})
manager.fetchAlbums { _ in
manager.fetchAssets() { [weak self] photos in

guard let `self` = self else { return }

self.updateEmptyView(count: photos.count)
self.title = self.title(forAlbum: manager.selectedAlbum)

if self.selectedArray.count > 0 {
self.collectionView.performBatchUpdates({ [weak self] in
self?.collectionView.reloadData()
}, completion: { [weak self] (finished) in
guard let `self` = self else { return }
// initialize preselected assets
self.selectedArray.forEach({ [weak self] (asset) in
if let row = photos.firstIndex(of: asset) {
let indexPathToSelect = IndexPath(row: row, section: 0)
self?.collectionView.selectItem(at: indexPathToSelect, animated: false, scrollPosition: UICollectionView.ScrollPosition(rawValue: 0))
}
})
self.updateSelectionCount()
})
} else {
self.collectionView.reloadData()
let item = self.collectionView(self.collectionView, numberOfItemsInSection: 0) - 1
let lastItemIndex = NSIndexPath(item: item, section: 0)
self.collectionView.scrollToItem(at: lastItemIndex as IndexPath, at: .top, animated: false)
}
self.loadingPlaceholderView.isHidden = true
self.loadingActivityIndicatorView.stopAnimating()
}
}

}

func setupGestureRecognizer() {
Expand Down Expand Up @@ -367,29 +408,37 @@ extension AssetsPhotoViewController {
}

func select(album: PHAssetCollection) {
if AssetsManager.shared.select(album: album) {
// set title with selected count if exists
if selectedArray.count > 0 {
updateNavigationStatus()
} else {
title = title(forAlbum: album)
}
collectionView.reloadData()

for asset in selectedArray {
if let index = AssetsManager.shared.assetArray.firstIndex(of: asset) {
logi("reselecting: \(index)")
collectionView.selectItem(at: IndexPath(row: index, section: 0), animated: false, scrollPosition: .init(rawValue: 0))
}
}
if AssetsManager.shared.assetArray.count > 0 {
if pickerConfig.assetsIsScrollToBottom == true {
collectionView.scrollToItem(at: IndexPath(row: AssetsManager.shared.assetArray.count - 1, section: 0), at: .bottom, animated: false)
loadingPlaceholderView.isHidden = false
loadingActivityIndicatorView.startAnimating()
AssetsManager.shared.selectAsync(album: album, complection: { [weak self] (result) in
guard let `self` = self else { return }
if result {

// set title with selected count if exists
if self.selectedArray.count > 0 {
self.updateNavigationStatus()
} else {
collectionView.scrollToItem(at: IndexPath(row: 0, section: 0), at: .bottom, animated: false)
self.title = self.title(forAlbum: album)
}
self.collectionView.reloadData()

for asset in self.selectedArray {
if let index = AssetsManager.shared.assetArray.firstIndex(of: asset) {
logi("reselecting: \(index)")
self.collectionView.selectItem(at: IndexPath(row: index, section: 0), animated: false, scrollPosition: .init(rawValue: 0))
}
}
if AssetsManager.shared.assetArray.count > 0 {
if self.pickerConfig.assetsIsScrollToBottom == true {
self.collectionView.scrollToItem(at: IndexPath(row: AssetsManager.shared.assetArray.count - 1, section: 0), at: .bottom, animated: false)
} else {
self.collectionView.scrollToItem(at: IndexPath(row: 0, section: 0), at: .bottom, animated: false)
}
}
}
}
self.loadingPlaceholderView.isHidden = true
self.loadingActivityIndicatorView.stopAnimating()
})
}

func select(asset: PHAsset, at indexPath: IndexPath) {
Expand Down Expand Up @@ -706,7 +755,9 @@ extension AssetsPhotoViewController: UICollectionViewDataSourcePrefetching {
public func collectionView(_ collectionView: UICollectionView, prefetchItemsAt indexPaths: [IndexPath]) {
var assets = [PHAsset]()
for indexPath in indexPaths {
assets.append(AssetsManager.shared.assetArray[indexPath.row])
if AssetsManager.shared.assetArray.count > indexPath.row {
assets.append(AssetsManager.shared.assetArray[indexPath.row])
}
}
AssetsManager.shared.cache(assets: assets, size: pickerConfig.assetCacheSize)
}
Expand Down

0 comments on commit a1b4d10

Please sign in to comment.