diff --git a/.DS_Store b/.DS_Store index 65d839a..bfa49d0 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/.gitignore b/.gitignore index d534044..1bf95b1 100644 --- a/.gitignore +++ b/.gitignore @@ -37,7 +37,9 @@ playground.xcworkspace # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. # Packages/ # Package.pins +Package.resolved .build/ +.swiftpm/ # CocoaPods # diff --git a/AssetsPickerViewController.podspec b/AssetsPickerViewController.podspec index f1cfe94..92f0124 100644 --- a/AssetsPickerViewController.podspec +++ b/AssetsPickerViewController.podspec @@ -8,7 +8,7 @@ Pod::Spec.new do |s| s.name = 'AssetsPickerViewController' - s.version = '2.9.6' + s.version = '2.9.7' s.summary = 'Picker controller that supports multiple photos and videos written in Swift.' # This description is used to generate tags and improve search results. @@ -41,5 +41,4 @@ Fully customizable UI. # s.public_header_files = 'Pod/Classes/**/*.h' # s.frameworks = 'UIKit', 'MapKit' s.dependency 'SnapKit' - s.dependency 'Device' end diff --git a/AssetsPickerViewController/Classes/Album/Controller/AssetsAlbumViewController.swift b/AssetsPickerViewController/Classes/Album/Controller/AssetsAlbumViewController.swift index ccb9d88..35b7cc5 100644 --- a/AssetsPickerViewController/Classes/Album/Controller/AssetsAlbumViewController.swift +++ b/AssetsPickerViewController/Classes/Album/Controller/AssetsAlbumViewController.swift @@ -116,7 +116,15 @@ open class AssetsAlbumViewController: UIViewController { make.edges.equalToSuperview() } - loadingPlaceholderView.isHidden = true + let isFetchedAlbums = AssetsManager.shared.isFetchedAlbums + if isFetchedAlbums { + loadingActivityIndicatorView.stopAnimating() + loadingPlaceholderView.isHidden = true + } else { + loadingActivityIndicatorView.startAnimating() + loadingPlaceholderView.isHidden = false + } + if #available(iOS 13.0, *) { loadingPlaceholderView.backgroundColor = .systemBackground } else { @@ -333,6 +341,14 @@ extension AssetsAlbumViewController: AssetsManagerDelegate { public func assetsManagerFetched(manager: AssetsManager) { collectionView.reloadData() + let isFetchedAlbums = AssetsManager.shared.isFetchedAlbums + if isFetchedAlbums { + loadingActivityIndicatorView.stopAnimating() + loadingPlaceholderView.isHidden = true + } else { + loadingActivityIndicatorView.startAnimating() + loadingPlaceholderView.isHidden = false + } } public func assetsManager(manager: AssetsManager, authorizationStatusChanged oldStatus: PHAuthorizationStatus, newStatus: PHAuthorizationStatus) {} @@ -366,4 +382,3 @@ extension AssetsAlbumViewController: AssetsManagerDelegate { public func assetsManager(manager: AssetsManager, removedAssets assets: [PHAsset], at indexPaths: [IndexPath]) {} public func assetsManager(manager: AssetsManager, updatedAssets assets: [PHAsset], at indexPaths: [IndexPath]) {} } - diff --git a/AssetsPickerViewController/Classes/Assets/AssetsManager+Sync.swift b/AssetsPickerViewController/Classes/Assets/AssetsManager+Sync.swift index c47552f..214b750 100644 --- a/AssetsPickerViewController/Classes/Assets/AssetsManager+Sync.swift +++ b/AssetsPickerViewController/Classes/Assets/AssetsManager+Sync.swift @@ -98,9 +98,13 @@ extension AssetsManager: PHPhotoLibraryChangeObserver { if let removedIndexesSet = assetsChangeDetails.removedIndexes { let removedIndexes = removedIndexesSet.asArray().sorted(by: { $0.row < $1.row }) var removedAssets = [PHAsset]() + let result = assetsChangeDetails.fetchResultAfterChanges for removedIndex in removedIndexes.reversed() { - removedAssets.insert(assetArray.remove(at: removedIndex.row), at: 0) + let asset = fetchResult.object(at: removedIndex.row) + removedAssets.insert(asset, at: 0) } + // update date source + self.fetchResult = result // stop caching for removed assets stopCache(assets: removedAssets, size: pickerConfig.assetCacheSize) notifySubscribers({ $0.assetsManager(manager: self, removedAssets: removedAssets, at: removedIndexes) }, condition: removedAssets.count > 0) @@ -109,11 +113,13 @@ extension AssetsManager: PHPhotoLibraryChangeObserver { if let insertedIndexesSet = assetsChangeDetails.insertedIndexes { let insertedIndexes = insertedIndexesSet.asArray().sorted(by: { $0.row < $1.row }) var insertedAssets = [PHAsset]() + let result = assetsChangeDetails.fetchResultAfterChanges for insertedIndex in insertedIndexes { - let insertedAsset = assetsChangeDetails.fetchResultAfterChanges.object(at: insertedIndex.row) + let insertedAsset = result.object(at: insertedIndex.row) insertedAssets.append(insertedAsset) - assetArray.insert(insertedAsset, at: insertedIndex.row) } + // update date source + self.fetchResult = result // start caching for inserted assets cache(assets: insertedAssets, size: pickerConfig.assetCacheSize) notifySubscribers({ $0.assetsManager(manager: self, insertedAssets: insertedAssets, at: insertedIndexes) }, condition: insertedAssets.count > 0) diff --git a/AssetsPickerViewController/Classes/Assets/AssetsManager.swift b/AssetsPickerViewController/Classes/Assets/AssetsManager.swift index 745295e..ff80d13 100644 --- a/AssetsPickerViewController/Classes/Assets/AssetsManager.swift +++ b/AssetsPickerViewController/Classes/Assets/AssetsManager.swift @@ -26,6 +26,10 @@ public protocol AssetsManagerDelegate: class { func assetsManager(manager: AssetsManager, updatedAssets assets: [PHAsset], at indexPaths: [IndexPath]) } +typealias AssetsFetchEntry = (albums: [PHFetchResult], fetchMap: [String: PHFetchResult], albumMap: [String: PHAssetCollection]) +typealias AssetsAlbumEntry = (fetchedAlbums: [PHAssetCollection], sortedAlbums: [PHAssetCollection], fetchResult: PHFetchResult) +typealias AssetsAlbumArrayEntry = (fetchedAlbumsArray: [[PHAssetCollection]], sortedAlbumsArray: [[PHAssetCollection]], albumsFetchArray: [PHFetchResult]) + // MARK: - AssetsManager open class AssetsManager: NSObject { @@ -50,14 +54,15 @@ open class AssetsManager: NSObject { var fetchedAlbumsArray = [[PHAssetCollection]]() /// stores sorted array by applying user defined comparator, it's in decreasing order by count by default, and it might same as fetchedAlbumsArray if AssetsPickerConfig has albumFetchOptions without albumComparator var sortedAlbumsArray = [[PHAssetCollection]]() - internal(set) open var assetArray = [PHAsset]() + internal(set) open var fetchResult: PHFetchResult? fileprivate(set) open var defaultAlbum: PHAssetCollection? fileprivate(set) open var cameraRollAlbum: PHAssetCollection! fileprivate(set) open var selectedAlbum: PHAssetCollection? - fileprivate var isFetchedAlbums: Bool = false + fileprivate(set) var isFetchedAlbums: Bool = false fileprivate var resourceLoadingQueue: DispatchQueue = DispatchQueue(label: "com.assetspicker.loader", qos: .userInitiated) + fileprivate var albumLoadingQueue: DispatchQueue = DispatchQueue(label: "com.assetspicker.album.loader", qos: .default) private override init() { super.init() @@ -82,7 +87,7 @@ open class AssetsManager: NSObject { sortedAlbumsArray.removeAll() // clear assets - assetArray.removeAll() + fetchResult = nil // clear fetch results albumsFetchArray.removeAll() @@ -251,7 +256,7 @@ extension AssetsManager { options.isNetworkAccessAllowed = true options.resizeMode = .exact return imageManager.requestImage( - for: assetArray[index], + for: fetchResult!.object(at: index), targetSize: size, contentMode: .aspectFill, options: options, @@ -332,13 +337,14 @@ extension AssetsManager { return false } self.selectedAlbum = newAlbum - if let fetchResult = fetchMap[newAlbum.localIdentifier] { - let indexSet = IndexSet(0.. Void) { + open func selectAsync(album newAlbum: PHAssetCollection, completion: @escaping (Bool, PHFetchResult?) -> Void) { if let oldAlbumIdentifier = self.selectedAlbum?.localIdentifier, oldAlbumIdentifier == newAlbum.localIdentifier { logi("Selected same album.") - completion(false, []) + completion(false, nil) } self.selectedAlbum = newAlbum - if let fetchResult = fetchMap[newAlbum.localIdentifier] { - resourceLoadingQueue.async { [weak self] in - let indexSet = IndexSet(0..]) -> Bool { + if let albumFilter = pickerConfig.albumFilter?[album.assetCollectionType], let fetchResult = fetchMap[album.localIdentifier] { + return albumFilter(album, fetchResult) + } + guard self.pickerConfig.albumIsShowHiddenAlbum || album.assetCollectionSubtype != .smartAlbumAllHidden else { + return false + } + guard let fetchResult = fetchMap[album.localIdentifier], self.pickerConfig.albumIsShowEmptyAlbum || fetchResult.count > 0 else { + return false + } + return true + } + func remove(album: PHAssetCollection? = nil, indexPath: IndexPath? = nil) { if let indexPath = indexPath { fetchedAlbumsArray[indexPath.section].remove(at: indexPath.row) @@ -430,6 +445,31 @@ extension AssetsManager { } } } + + func sortedAlbums(fromAlbums albums: [PHAssetCollection], filterBy fetchMap: [String: PHFetchResult]) -> [PHAssetCollection] { + guard let albumType = albums.first?.assetCollectionType else { + return albums + } + let filtered = albums.filter { self.isQualified(album: $0, filterBy: fetchMap) } + if let comparator = pickerConfig.albumComparator { + return filtered.sorted(by: { (leftAlbum, rightAlbum) -> Bool in + if let leftResult = fetchMap[leftAlbum.localIdentifier], let rightResult = fetchMap[rightAlbum.localIdentifier] { + return comparator(leftAlbum.assetCollectionType, (leftAlbum, leftResult), (rightAlbum, rightResult)) + } else { + logw("Failed to get fetch result from fetchMap. Please raise an issue if you've met this message.") + return true + } + }) + } else { + if let _ = pickerConfig.albumFetchOptions?[albumType] { + // return fetched album as it is + return filtered + } else { + // default: by count + return filtered.sorted(by: { Int((fetchMap[$0.localIdentifier]?.count) ?? 0) > Int((fetchMap[$1.localIdentifier]?.count) ?? 0) }) + } + } + } } // MARK: - Check @@ -520,18 +560,22 @@ extension AssetsManager { guard let `self` = self else { return } if !self.isFetchedAlbums { - let smartAlbumEntry = self.fetchAlbums(forAlbumType: .smartAlbum) + var types: [PHAssetCollectionType] = [ .smartAlbum, .album ] + + let smartAlbumEntry = self.fetchDefaultAlbums(forAlbumType: .smartAlbum) self.fetchedAlbumsArray.append(smartAlbumEntry.fetchedAlbums) self.sortedAlbumsArray.append(smartAlbumEntry.sortedAlbums) self.albumsFetchArray.append(smartAlbumEntry.fetchResult) - let albumEntry = self.fetchAlbums(forAlbumType: .album) + let albumEntry = self.fetchDefaultAlbums(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) + types.append(.moment) + + let momentEntry = self.fetchDefaultAlbums(forAlbumType: .moment) self.fetchedAlbumsArray.append(momentEntry.fetchedAlbums) self.sortedAlbumsArray.append(momentEntry.sortedAlbums) self.albumsFetchArray.append(momentEntry.fetchResult) @@ -542,7 +586,20 @@ extension AssetsManager { delegate.assetsManagerFetched(manager: self) } } - self.isFetchedAlbums = true + + self.fetchAllAlbums(types: types) { (result) in + self.fetchedAlbumsArray = result.fetchedAlbumsArray + self.sortedAlbumsArray = result.sortedAlbumsArray + self.albumsFetchArray = result.albumsFetchArray + + self.subscribers.forEach { [weak self] (delegate) in + guard let `self` = self else { return } + DispatchQueue.main.async { + delegate.assetsManagerFetched(manager: self) + } + } + self.isFetchedAlbums = true + } } // notify DispatchQueue.main.async { @@ -551,30 +608,129 @@ extension AssetsManager { } } - open func fetchAssets(isRefetch: Bool = false, completion: (([PHAsset]) -> Void)? = nil) { + func fetchAllAlbums(types: [PHAssetCollectionType], complection: @escaping (AssetsAlbumArrayEntry) -> Void ) { + + let queue = DispatchQueue.global(qos: .userInitiated) + + var fetchedAlbumsArray: [[PHAssetCollection]] = [] + var sortedAlbumsArray: [[PHAssetCollection]] = [] + var albumsFetchArray: [PHFetchResult] = [] + + let group = DispatchGroup() + + var fetchMap = [String: PHFetchResult]() + var albumMap = [String: PHAssetCollection]() + + for type in types { + queue.async(group: group) { + group.enter() + self.fetchAlbumsAsync(forAlbumType: type) { (albumsArrayEntry, fetchedEntry) in + fetchedAlbumsArray.append(albumsArrayEntry.fetchedAlbums) + sortedAlbumsArray.append(albumsArrayEntry.sortedAlbums) + albumsFetchArray.append(albumsArrayEntry.fetchResult) + + fetchMap = fetchMap.merging(fetchedEntry.fetchMap) { (first, second) -> PHFetchResult in return first } + albumMap = albumMap.merging(fetchedEntry.albumMap) { (first, second) -> PHAssetCollection in return first } + + group.leave() + } + } + } + + group.notify(queue: .main) { + self.fetchMap = fetchMap + self.albumMap = albumMap + let result = (fetchedAlbumsArray, sortedAlbumsArray, albumsFetchArray) + complection(result) + } + } + + open func fetchAssets(isRefetch: Bool = false, completion: ((PHFetchResult?) -> Void)? = nil) { fetchAlbums(isRefetch: isRefetch, completion: { [weak self] _ in guard let `self` = self else { return } if isRefetch { - self.assetArray.removeAll() + self.fetchResult = nil } // set default album - self.selectAsync(album: self.defaultAlbum ?? self.cameraRollAlbum) { result, photos in - completion?(photos) + self.selectAsync(album: self.defaultAlbum ?? self.cameraRollAlbum) { successful, result in + completion?(result) } }) } + func fetchDefaultAlbums(forAlbumType type: PHAssetCollectionType) -> (fetchedAlbums: [PHAssetCollection], sortedAlbums: [PHAssetCollection], fetchResult: PHFetchResult) { + + let fetchOption = pickerConfig.albumFetchOptions?[type] + let albumFetchResult = PHAssetCollection.fetchAssetCollections(with: type, subtype: .any, options: fetchOption) + var fetchedAlbums = [PHAssetCollection]() + + let indexSet = IndexSet(integersIn: 0.. (fetchedAlbums: [PHAssetCollection], sortedAlbums: [PHAssetCollection], fetchResult: PHFetchResult) { let fetchOption = pickerConfig.albumFetchOptions?[type] let albumFetchResult = PHAssetCollection.fetchAssetCollections(with: type, subtype: .any, options: fetchOption) var fetchedAlbums = [PHAssetCollection]() - albumFetchResult.enumerateObjects({ (album, _, _) in + let indexSet = IndexSet(integersIn: 0.. Void) { + + let fetchOption = pickerConfig.albumFetchOptions?[type] + let albumFetchResult = PHAssetCollection.fetchAssetCollections(with: type, subtype: .any, options: fetchOption) + var fetchedAlbums = [PHAssetCollection]() + + let indexSet = IndexSet(integersIn: 0.. PHFetchResult { @@ -631,6 +844,29 @@ extension AssetsManager { return fetchResult } + func fetchAlbumAsync(albums: [PHAssetCollection], completion: @escaping (AssetsFetchEntry) -> Void) { + + self.albumLoadingQueue.async { + + var resuls: [PHFetchResult] = [] + var fetchMap = [String: PHFetchResult]() + var albumMap = [String: PHAssetCollection]() + + for album in albums { + let fetchResult = PHAsset.fetchAssets(in: album, options: self.pickerConfig.assetFetchOptions?[album.assetCollectionType]) + + // cache fetch result + fetchMap[album.localIdentifier] = fetchResult + + // cache album + albumMap[album.localIdentifier] = album + + resuls.append(fetchResult) + } + + completion((resuls, fetchMap, albumMap)) + } + } } // MARK: - IndexSet Utility diff --git a/AssetsPickerViewController/Classes/Library/Extensions/UIView+Dimmer.swift b/AssetsPickerViewController/Classes/Library/Extensions/UIView+Dimmer.swift index 43eef5f..8dbd70e 100644 --- a/AssetsPickerViewController/Classes/Library/Extensions/UIView+Dimmer.swift +++ b/AssetsPickerViewController/Classes/Library/Extensions/UIView+Dimmer.swift @@ -6,6 +6,7 @@ // // +import UIKit import SnapKit fileprivate let kDimmerViewKey = "kDimmerViewKey" diff --git a/AssetsPickerViewController/Classes/Photo/Controller/AssetsPhotoViewController+AssetsManager.swift b/AssetsPickerViewController/Classes/Photo/Controller/AssetsPhotoViewController+AssetsManager.swift index ef280ba..dd3dfc0 100644 --- a/AssetsPickerViewController/Classes/Photo/Controller/AssetsPhotoViewController+AssetsManager.swift +++ b/AssetsPickerViewController/Classes/Photo/Controller/AssetsPhotoViewController+AssetsManager.swift @@ -115,11 +115,18 @@ extension AssetsPhotoViewController { completion(nil) return } - guard let savedAssetEntry = AssetsManager.shared.assetArray.enumerated().first(where: { $0.element.localIdentifier == newlySavedIdentifier }) else { - completion(nil) + var index: Int = NSNotFound + guard let fetchResult = AssetsManager.shared.fetchResult else { return } + fetchResult.enumerateObjects { (asset, idx, stop) in + if asset.localIdentifier == newlySavedIdentifier { + index = idx + stop.pointee = true + } + } + if index == NSNotFound { return } - let ip = IndexPath(row: savedAssetEntry.offset, section: 0) + let ip = IndexPath(row: index, section: 0) indexPathToSelect = ip if selectedArray.count < pickerConfig.assetsMaximumSelectionCount { select(at: ip) diff --git a/AssetsPickerViewController/Classes/Photo/Controller/AssetsPhotoViewController+Collection.swift b/AssetsPickerViewController/Classes/Photo/Controller/AssetsPhotoViewController+Collection.swift index 29de2b5..286743d 100644 --- a/AssetsPickerViewController/Classes/Photo/Controller/AssetsPhotoViewController+Collection.swift +++ b/AssetsPickerViewController/Classes/Photo/Controller/AssetsPhotoViewController+Collection.swift @@ -12,7 +12,7 @@ import Photos extension AssetsPhotoViewController: UICollectionViewDataSource { public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { - let count = AssetsManager.shared.assetArray.count + let count = AssetsManager.shared.fetchResult?.count ?? 0 updateEmptyView(count: count) return count } @@ -23,11 +23,14 @@ extension AssetsPhotoViewController: UICollectionViewDataSource { logw("Failed to cast UICollectionViewCell.") return cell } - let asset = AssetsManager.shared.assetArray[indexPath.row] - photoCell.asset = asset - photoCell.isVideo = asset.mediaType == .video - if photoCell.isVideo { - photoCell.duration = asset.duration + if let asset = AssetsManager.shared.fetchResult?.object(at: indexPath.row) { + photoCell.asset = asset + photoCell.isVideo = asset.mediaType == .video + if photoCell.isVideo { + photoCell.duration = asset.duration + } + } else { + photoCell.asset = nil } if #available(iOS 13.0, *) { @@ -44,17 +47,20 @@ extension AssetsPhotoViewController: UICollectionViewDataSource { return } - let asset = AssetsManager.shared.assetArray[indexPath.row] - photoCell.asset = asset - photoCell.isVideo = asset.mediaType == .video - if photoCell.isVideo { - photoCell.duration = asset.duration - } - - if let selectedAsset = selectedMap[asset.localIdentifier] { - if let targetIndex = selectedArray.firstIndex(of: selectedAsset) { - photoCell.count = targetIndex + 1 + if let asset = AssetsManager.shared.fetchResult?.object(at: indexPath.row) { + photoCell.asset = asset + photoCell.isVideo = asset.mediaType == .video + if photoCell.isVideo { + photoCell.duration = asset.duration + } + + if let selectedAsset = selectedMap[asset.localIdentifier] { + if let targetIndex = selectedArray.firstIndex(of: selectedAsset) { + photoCell.count = targetIndex + 1 + } } + } else { + photoCell.asset = nil } tryFetchImage(forCell: photoCell, forIndexPath: indexPath) @@ -129,8 +135,10 @@ extension AssetsPhotoViewController: UICollectionViewDataSourcePrefetching { public func collectionView(_ collectionView: UICollectionView, prefetchItemsAt indexPaths: [IndexPath]) { var assets = [PHAsset]() for indexPath in indexPaths { - if AssetsManager.shared.assetArray.count > indexPath.row { - assets.append(AssetsManager.shared.assetArray[indexPath.row]) + let count = AssetsManager.shared.fetchResult?.count ?? 0 + if count > indexPath.row { + guard let asset = AssetsManager.shared.fetchResult?.object(at: indexPath.row) else { return } + assets.append(asset) } } AssetsManager.shared.cache(assets: assets, size: pickerConfig.assetCacheSize) diff --git a/AssetsPickerViewController/Classes/Photo/Controller/AssetsPhotoViewController+Delegate.swift b/AssetsPickerViewController/Classes/Photo/Controller/AssetsPhotoViewController+Delegate.swift index d0dbaa5..c148f19 100644 --- a/AssetsPickerViewController/Classes/Photo/Controller/AssetsPhotoViewController+Delegate.swift +++ b/AssetsPickerViewController/Classes/Photo/Controller/AssetsPhotoViewController+Delegate.swift @@ -45,11 +45,12 @@ extension AssetsPhotoViewController: UIGestureRecognizerDelegate { } // MARK: - UIScrollViewDelegate -//extension AssetsPhotoViewController: UIScrollViewDelegate { -// public func scrollViewDidScroll(_ scrollView: UIScrollView) { +extension AssetsPhotoViewController: UIScrollViewDelegate { + public func scrollViewDidScroll(_ scrollView: UIScrollView) { // logi("contentOffset: \(scrollView.contentOffset)") -// } -//} + updateCachedAssets() + } +} // MARK: - AssetsAlbumViewControllerDelegate extension AssetsPhotoViewController: AssetsAlbumViewControllerDelegate { @@ -71,7 +72,8 @@ extension AssetsPhotoViewController: UIContextMenuInteractionDelegate { let pointInCollectionView = self.collectionView.convert(location, from: interaction.view) guard let pressingIndexPath = self.collectionView.indexPathForItem(at: pointInCollectionView) else { return nil } let previewController = AssetsPreviewController() - previewController.asset = AssetsManager.shared.assetArray[pressingIndexPath.row] + guard let fetchResult = AssetsManager.shared.fetchResult else { return nil } + previewController.asset = fetchResult.object(at: pressingIndexPath.row) return previewController }, actionProvider: nil) } @@ -88,7 +90,8 @@ extension AssetsPhotoViewController: UIViewControllerPreviewingDelegate { guard let pressingCell = collectionView.cellForItem(at: pressingIndexPath) else { return nil } previewingContext.sourceRect = pressingCell.frame let previewController = AssetsPreviewController() - previewController.asset = AssetsManager.shared.assetArray[pressingIndexPath.row] + guard let fetchResult = AssetsManager.shared.fetchResult else { return nil } + previewController.asset = fetchResult.object(at: pressingIndexPath.row) return previewController } diff --git a/AssetsPickerViewController/Classes/Photo/Controller/AssetsPhotoViewController+Model.swift b/AssetsPickerViewController/Classes/Photo/Controller/AssetsPhotoViewController+Model.swift index 4376006..8ca1801 100644 --- a/AssetsPickerViewController/Classes/Photo/Controller/AssetsPhotoViewController+Model.swift +++ b/AssetsPickerViewController/Classes/Photo/Controller/AssetsPhotoViewController+Model.swift @@ -36,8 +36,10 @@ extension AssetsPhotoViewController { func isSelected(at indexPath: IndexPath) -> Bool { let manager = AssetsManager.shared - guard indexPath.row < manager.assetArray.count else { return false } - if let _ = selectedMap[manager.assetArray[indexPath.row].localIdentifier] { + guard let fetchResult = manager.fetchResult else { return false } + guard indexPath.row < fetchResult.count else { return false } + let asset = fetchResult.object(at: indexPath.row) + if let _ = selectedMap[asset.localIdentifier] { return true } else { return false @@ -47,8 +49,9 @@ extension AssetsPhotoViewController { func select(at indexPath: IndexPath) { defer { logSelectStatus(indexPath, isSelect: true) } let manager = AssetsManager.shared - guard indexPath.row < manager.assetArray.count else { return } - let asset = manager.assetArray[indexPath.row] + guard let fetchResult = manager.fetchResult else { return } + guard indexPath.row < fetchResult.count else { return } + let asset = fetchResult.object(at: indexPath.row) if let _ = selectedMap[asset.localIdentifier] {} else { selectedArray.append(asset) selectedMap[asset.localIdentifier] = asset @@ -61,8 +64,9 @@ extension AssetsPhotoViewController { func deselect(at indexPath: IndexPath) { defer { logSelectStatus(indexPath, isSelect: false) } let manager = AssetsManager.shared - guard indexPath.row < manager.assetArray.count else { return } - let asset = manager.assetArray[indexPath.row] + guard let fetchResult = manager.fetchResult else { return } + guard indexPath.row < fetchResult.count else { return } + let asset = fetchResult.object(at: indexPath.row) guard let targetAsset = selectedMap[asset.localIdentifier] else { logw("Invalid status.") return diff --git a/AssetsPickerViewController/Classes/Photo/Controller/AssetsPhotoViewController+Selection.swift b/AssetsPickerViewController/Classes/Photo/Controller/AssetsPhotoViewController+Selection.swift index f63f7a5..b1e2a4c 100644 --- a/AssetsPickerViewController/Classes/Photo/Controller/AssetsPhotoViewController+Selection.swift +++ b/AssetsPickerViewController/Classes/Photo/Controller/AssetsPhotoViewController+Selection.swift @@ -32,7 +32,8 @@ extension AssetsPhotoViewController: UICollectionViewDelegate { if LogConfig.isSelectLogEnabled { logi("shouldSelectItemAt: \(indexPath.row)") } if let delegate = self.delegate { - let shouldSelect = delegate.assetsPicker?(controller: picker, shouldSelect: AssetsManager.shared.assetArray[indexPath.row], at: indexPath) ?? true + guard let fetchResult = AssetsManager.shared.fetchResult else { return false } + let shouldSelect = delegate.assetsPicker?(controller: picker, shouldSelect: fetchResult.object(at: indexPath.row), at: indexPath) ?? true guard shouldSelect else { return false } } @@ -62,7 +63,8 @@ extension AssetsPhotoViewController: UICollectionViewDelegate { public func collectionView(_ collectionView: UICollectionView, shouldDeselectItemAt indexPath: IndexPath) -> Bool { if LogConfig.isSelectLogEnabled { logi("shouldDeselectItemAt: \(indexPath.row)") } if let delegate = self.delegate { - let shouldDeselect = delegate.assetsPicker?(controller: picker, shouldDeselect: AssetsManager.shared.assetArray[indexPath.row], at: indexPath) ?? true + guard let fetchResult = AssetsManager.shared.fetchResult else { return false } + let shouldDeselect = delegate.assetsPicker?(controller: picker, shouldDeselect: fetchResult.object(at: indexPath.row), at: indexPath) ?? true guard shouldDeselect else { return false } } deselect(at: indexPath) @@ -86,7 +88,8 @@ extension AssetsPhotoViewController { return } for selectedIndexPath in indexPathsForSelectedItems { - if let _ = selectedMap[AssetsManager.shared.assetArray[selectedIndexPath.row].localIdentifier] { + guard let fetchResult = AssetsManager.shared.fetchResult else { return } + if let _ = selectedMap[fetchResult.object(at: selectedIndexPath.row).localIdentifier] { } else { loge("selected item not found in local map!") diff --git a/AssetsPickerViewController/Classes/Photo/Controller/AssetsPhotoViewController+Setup.swift b/AssetsPickerViewController/Classes/Photo/Controller/AssetsPhotoViewController+Setup.swift index 611fe4d..30cee68 100644 --- a/AssetsPickerViewController/Classes/Photo/Controller/AssetsPhotoViewController+Setup.swift +++ b/AssetsPickerViewController/Classes/Photo/Controller/AssetsPhotoViewController+Setup.swift @@ -73,13 +73,15 @@ extension AssetsPhotoViewController { let manager = AssetsManager.shared manager.subscribe(subscriber: self) manager.fetchAlbums { _ in - manager.fetchAssets() { [weak self] photos in + manager.fetchAssets() { [weak self] result in guard let `self` = self else { return } - self.updateEmptyView(count: photos.count) + guard let fetchResult = result else { return } + self.updateEmptyView(count: fetchResult.count) self.updateNavigationStatus() self.collectionView.reloadData() - self.preselectItemsIfNeeded(photos: photos) + self.preselectItemsIfNeeded(result: fetchResult) self.scrollToLastItemIfNeeded() + self.updateCachedAssets(force: true) // hide loading self.loadingPlaceholderView.isHidden = true self.loadingActivityIndicatorView.stopAnimating() diff --git a/AssetsPickerViewController/Classes/Photo/Controller/AssetsPhotoViewController+UI.swift b/AssetsPickerViewController/Classes/Photo/Controller/AssetsPhotoViewController+UI.swift index d7b8e95..443e07d 100644 --- a/AssetsPickerViewController/Classes/Photo/Controller/AssetsPhotoViewController+UI.swift +++ b/AssetsPickerViewController/Classes/Photo/Controller/AssetsPhotoViewController+UI.swift @@ -57,7 +57,9 @@ extension AssetsPhotoViewController { } func deselectOldestIfNeeded(isForced: Bool = false) { - if selectedArray.count > pickerConfig.assetsMaximumSelectionCount, let firstSelectedAsset = selectedArray.first, let indexToDeselect = AssetsManager.shared.assetArray.firstIndex(of: firstSelectedAsset) { + guard let fetchResult = AssetsManager.shared.fetchResult else { return } + if selectedArray.count > pickerConfig.assetsMaximumSelectionCount, let firstSelectedAsset = selectedArray.first { + let indexToDeselect = fetchResult.index(of: firstSelectedAsset) let indexPathToDeselect = IndexPath(row: indexToDeselect, section: 0) deselect(at: indexPathToDeselect) deselectCell(at: indexPathToDeselect, isForced: isForced) @@ -67,12 +69,13 @@ extension AssetsPhotoViewController { func select(album: PHAssetCollection) { loadingPlaceholderView.isHidden = false loadingActivityIndicatorView.startAnimating() - AssetsManager.shared.selectAsync(album: album, completion: { [weak self] (result, photos) in + AssetsManager.shared.selectAsync(album: album, completion: { [weak self] (successful, result) in guard let `self` = self else { return } - guard result else { return } + guard successful else { return } + guard let fetchResult = result else { return } self.collectionView.reloadData() self.scrollToLastItemIfNeeded() - self.preselectItemsIfNeeded(photos: photos) + self.preselectItemsIfNeeded(result: fetchResult) self.updateNavigationStatus() self.loadingPlaceholderView.isHidden = true self.loadingActivityIndicatorView.stopAnimating() @@ -90,26 +93,25 @@ extension AssetsPhotoViewController { func scrollToLastItemIfNeeded() { - let assets = AssetsManager.shared.assetArray - guard !assets.isEmpty else { return } + guard let fetchResult = AssetsManager.shared.fetchResult else { return } + guard !(fetchResult.count == 0) else { return } if pickerConfig.assetsIsScrollToBottom == true { - self.collectionView.scrollToItem(at: IndexPath(row: assets.count - 1, section: 0), at: .bottom, animated: false) + self.collectionView.scrollToItem(at: IndexPath(row: fetchResult.count - 1, section: 0), at: .bottom, animated: false) } else { self.collectionView.scrollToItem(at: IndexPath(row: 0, section: 0), at: .bottom, animated: false) } } - func preselectItemsIfNeeded(photos: [PHAsset]) { + func preselectItemsIfNeeded(result: PHFetchResult) { if selectedArray.count > 0 { // initialize preselected assets selectedArray.forEach({ [weak self] (asset) in - if let row = photos.firstIndex(of: asset) { - let indexPathToSelect = IndexPath(row: row, section: 0) - let scrollPosition = UICollectionView.ScrollPosition(rawValue: 0) - self?.collectionView.selectItem(at: indexPathToSelect, - animated: false, - scrollPosition: scrollPosition) - } + let row = result.index(of: asset) + let indexPathToSelect = IndexPath(row: row, section: 0) + let scrollPosition = UICollectionView.ScrollPosition(rawValue: 0) + self?.collectionView.selectItem(at: indexPathToSelect, + animated: false, + scrollPosition: scrollPosition) }) updateSelectionCount() } @@ -117,13 +119,13 @@ extension AssetsPhotoViewController { func updateSelectionCount() { let visibleIndexPaths = collectionView.indexPathsForVisibleItems - let assets = AssetsManager.shared.assetArray + guard let fetchResult = AssetsManager.shared.fetchResult else { return } for visibleIndexPath in visibleIndexPaths { - guard assets.count > visibleIndexPath.row else { - loge("Referred wrong index\(visibleIndexPath.row) while asset count is \(assets.count).") + guard fetchResult.count > visibleIndexPath.row else { + loge("Referred wrong index\(visibleIndexPath.row) while asset count is \(fetchResult.count).") break } - if let selectedAsset = selectedMap[assets[visibleIndexPath.row].localIdentifier], var photoCell = collectionView.cellForItem(at: visibleIndexPath) as? AssetsPhotoCellProtocol { + if let selectedAsset = selectedMap[fetchResult.object(at: visibleIndexPath.row).localIdentifier], var photoCell = collectionView.cellForItem(at: visibleIndexPath) as? AssetsPhotoCellProtocol { if let selectedIndex = selectedArray.firstIndex(of: selectedAsset) { photoCell.count = selectedIndex + 1 } @@ -132,13 +134,11 @@ extension AssetsPhotoViewController { } func updateNavigationStatus() { + doneButtonItem.isEnabled = selectedArray.count >= (pickerConfig.assetsMinimumSelectionCount >= 0 ? pickerConfig.assetsMinimumSelectionCount : 1) if let album = AssetsManager.shared.selectedAlbum, selectedArray.isEmpty { title = self.title(forAlbum: album) } else { - - doneButtonItem.isEnabled = selectedArray.count >= (pickerConfig.assetsMinimumSelectionCount >= 0 ? pickerConfig.assetsMinimumSelectionCount : 1) - let counts: (imageCount: Int, videoCount: Int) = selectedArray.reduce((0, 0)) { (result, asset) -> (Int, Int) in let imageCount = asset.mediaType == .image ? 1 : 0 let videoCount = asset.mediaType == .video ? 1 : 0 @@ -196,4 +196,97 @@ extension AssetsPhotoViewController { } return titleString } + + func updateCachedAssets(force: Bool = false) { + let isViewVisible = isViewLoaded && view.window != nil + + if !isViewVisible { + return + } + + let bounds = collectionView.bounds + + // The preheat window is twice the height of the visible rect + var preheatRect = bounds + preheatRect = preheatRect.insetBy(dx: 0.0, dy: -0.5 * preheatRect.height) + + // If scrolled by a "reasonable" amount... + let delta = abs(preheatRect.midY - previousPreheatRect.midY) + + if (delta > (bounds.height / 3.0)) || force { + var addedIndexPaths: [IndexPath] = [] + var removedIndexPaths: [IndexPath] = [] + + computeDifferenceBetweenRect(previousPreheatRect, newRect: preheatRect, added: { (rect) in + let indexPaths = getIndexPathsForElements(in: rect) + addedIndexPaths.append(contentsOf: indexPaths) + }) { (rect) in + let indexPaths = getIndexPathsForElements(in: rect) + removedIndexPaths.append(contentsOf: indexPaths) + } + + let assetsToStartCaching = getAssets(at: addedIndexPaths) + let assetsToStopCaching = getAssets(at: removedIndexPaths) + + let targetSize = pickerConfig.assetCacheSize + AssetsManager.shared.cache(assets: assetsToStartCaching, size: targetSize) + AssetsManager.shared.stopCache(assets: assetsToStopCaching, size: targetSize) + self.previousPreheatRect = preheatRect + } + } + + func getIndexPathsForElements(in rect: CGRect) -> [IndexPath] { + let allLayoutAttributes = collectionView.collectionViewLayout.layoutAttributesForElements(in: rect) + guard let attributes = allLayoutAttributes else { return [] } + guard attributes.count == 0 else { return [] } + var indexPaths: [IndexPath] = [] + for attribut in attributes { + indexPaths.append(attribut.indexPath) + } + return indexPaths + } + + func getAssets(at indexPaths: [IndexPath]) -> [PHAsset] { + if indexPaths.count == 0 { return [] } + guard let fetchResult = AssetsManager.shared.fetchResult else { return [] } + var asstes: [PHAsset] = [] + for indexPath in indexPaths { + if indexPath.item < fetchResult.count && indexPath.item != 0 { + let index = fetchResult.count - indexPath.item + let asset = fetchResult.object(at: index) + asstes.append(asset) + } + } + return asstes + } + + func computeDifferenceBetweenRect(_ oldRect: CGRect, newRect: CGRect, added: (CGRect) -> Void, removed: (CGRect) -> Void) { + if newRect.intersects(oldRect) { + let oldMaxY = oldRect.maxY + let oldMinY = oldRect.minY + let newMaxY = newRect.maxY + let newMinY = newRect.minY + + if newMaxY > oldMaxY { + let rect = CGRect(x: newRect.origin.x, y: oldMaxY, width: newRect.size.width, height: (newMaxY - oldMaxY)) + added(rect) + } + if oldMinY > newMinY { + let rect = CGRect(x: newRect.origin.x, y: newMinY, width: newRect.size.width, height: oldMinY - newMinY) + added(rect) + } + if newMaxY < oldMaxY { + let rect = CGRect(x: newRect.origin.x, y: newMaxY, width: newRect.size.width, height: oldMaxY - newMaxY) + removed(rect) + } + if oldMinY < newMinY { + let rect = CGRect(x: newRect.origin.x, y: oldMinY, width: newRect.size.width, height: newMinY - oldMinY) + removed(rect) + } + + } else { + added(newRect) + removed(oldRect) + } + } } diff --git a/AssetsPickerViewController/Classes/Photo/Controller/AssetsPhotoViewController.swift b/AssetsPickerViewController/Classes/Photo/Controller/AssetsPhotoViewController.swift index b2848e4..4f60673 100644 --- a/AssetsPickerViewController/Classes/Photo/Controller/AssetsPhotoViewController.swift +++ b/AssetsPickerViewController/Classes/Photo/Controller/AssetsPhotoViewController.swift @@ -9,7 +9,6 @@ import UIKit import Photos import PhotosUI -import Device import SnapKit // MARK: - AssetsPhotoViewController @@ -30,6 +29,8 @@ open class AssetsPhotoViewController: UIViewController { let fetchService = AssetsFetchService() + var previousPreheatRect: CGRect = .zero + lazy var cancelButtonItem: UIBarButtonItem = { let buttonItem = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, @@ -155,6 +156,7 @@ open class AssetsPhotoViewController: UIViewController { guard let `self` = self else { return } self.updateNoPermissionView() if isGranted { + AssetsManager.shared.registerObserver() self.setupAssets() } else { self.delegate?.assetsPickerCannotAccessPhotoLibrary?(controller: self.picker) @@ -180,7 +182,8 @@ open class AssetsPhotoViewController: UIViewController { super.viewDidLayoutSubviews() if !didSetInitialPosition { if pickerConfig.assetsIsScrollToBottom { - let count = AssetsManager.shared.assetArray.count + guard let fetchResult = AssetsManager.shared.fetchResult else { return } + let count = fetchResult.count if count > 0 { if self.collectionView.collectionViewLayout.collectionViewContentSize.height > 0 { let lastRow = self.collectionView.numberOfItems(inSection: 0) - 1 @@ -194,13 +197,13 @@ open class AssetsPhotoViewController: UIViewController { open func deselectAll() { var indexPaths = [IndexPath]() + guard let fetchResult = AssetsManager.shared.fetchResult else { return } for selectedAsset in selectedArray { - if let row = AssetsManager.shared.assetArray.firstIndex(of: selectedAsset) { - let indexPath = IndexPath(row: row, section: 0) - deselectCell(at: indexPath) - delegate?.assetsPicker?(controller: picker, didDeselect: selectedAsset, at: indexPath) - indexPaths.append(indexPath) - } + let row = fetchResult.index(of: selectedAsset) + let indexPath = IndexPath(row: row, section: 0) + deselectCell(at: indexPath) + delegate?.assetsPicker?(controller: picker, didDeselect: selectedAsset, at: indexPath) + indexPaths.append(indexPath) } updateSelectionCount() updateNavigationStatus() diff --git a/AssetsPickerViewController/Classes/Photo/View/AssetsPhotoLayout.swift b/AssetsPickerViewController/Classes/Photo/View/AssetsPhotoLayout.swift index f6f63f8..7ae2ad4 100644 --- a/AssetsPickerViewController/Classes/Photo/View/AssetsPhotoLayout.swift +++ b/AssetsPickerViewController/Classes/Photo/View/AssetsPhotoLayout.swift @@ -7,7 +7,6 @@ // import UIKit -import Device open class AssetsPhotoLayout: UICollectionViewFlowLayout { @@ -39,20 +38,21 @@ open class AssetsPhotoLayout: UICollectionViewFlowLayout { extension AssetsPhotoLayout { open func expectedContentHeight(forViewSize size: CGSize, isPortrait: Bool) -> CGFloat { - var rows = AssetsManager.shared.assetArray.count / (isPortrait ? pickerConfig.assetPortraitColumnCount : pickerConfig.assetLandscapeColumnCount) - let remainder = AssetsManager.shared.assetArray.count % (isPortrait ? pickerConfig.assetPortraitColumnCount : pickerConfig.assetLandscapeColumnCount) + guard let fetchResult = AssetsManager.shared.fetchResult else { return 0.0 } + var rows = fetchResult.count / (isPortrait ? pickerConfig.assetPortraitColumnCount : pickerConfig.assetLandscapeColumnCount) + let remainder = fetchResult.count % (isPortrait ? pickerConfig.assetPortraitColumnCount : pickerConfig.assetLandscapeColumnCount) rows += remainder > 0 ? 1 : 0 let cellSize = isPortrait ? pickerConfig.assetPortraitCellSize(forViewSize: UIScreen.main.portraitContentSize) : pickerConfig.assetLandscapeCellSize(forViewSize: UIScreen.main.landscapeContentSize) let lineSpace = isPortrait ? pickerConfig.assetPortraitLineSpace : pickerConfig.assetLandscapeLineSpace let contentHeight = CGFloat(rows) * cellSize.height + (CGFloat(max(rows - 1, 0)) * lineSpace) - let bottomHeight = cellSize.height * 2/3 + Device.safeAreaInsets(isPortrait: isPortrait).bottom + let bottomHeight = cellSize.height * 2/3 + UIScreen.safeAreaInsets(isPortrait: isPortrait).bottom return contentHeight + bottomHeight } private func offsetRatio(collectionView: UICollectionView, offset: CGPoint, contentSize: CGSize, isPortrait: Bool) -> CGFloat { - return (offset.y > 0 ? offset.y : 0) / ((contentSize.height + Device.safeAreaInsets(isPortrait: isPortrait).bottom) - collectionView.bounds.height) + return (offset.y > 0 ? offset.y : 0) / ((contentSize.height + UIScreen.safeAreaInsets(isPortrait: isPortrait).bottom) - collectionView.bounds.height) } open func translateOffset(forChangingSize size: CGSize, currentOffset: CGPoint) -> CGPoint? { @@ -67,8 +67,8 @@ extension AssetsPhotoLayout { var futureOffsetY = (contentHeight - size.height) * currentRatio if currentOffset.y < 0 { - let insetRatio = (-currentOffset.y) / Device.safeAreaInsets(isPortrait: isPortraitCurrent).top - let insetDiff = Device.safeAreaInsets(isPortrait: isPortraitFuture).top * insetRatio + let insetRatio = (-currentOffset.y) / UIScreen.safeAreaInsets(isPortrait: isPortraitCurrent).top + let insetDiff = UIScreen.safeAreaInsets(isPortrait: isPortraitFuture).top * insetRatio futureOffsetY -= insetDiff } diff --git a/AssetsPickerViewController/Classes/Photo/View/PanoramaIconView.swift b/AssetsPickerViewController/Classes/Photo/View/PanoramaIconView.swift index 9182127..5ad7a4d 100644 --- a/AssetsPickerViewController/Classes/Photo/View/PanoramaIconView.swift +++ b/AssetsPickerViewController/Classes/Photo/View/PanoramaIconView.swift @@ -6,6 +6,7 @@ // import Foundation +import UIKit open class PanoramaIconView: UIView { diff --git a/AssetsPickerViewController/Classes/Picker/AssetsCameraManager.swift b/AssetsPickerViewController/Classes/Picker/AssetsCameraManager.swift index 3d027ff..2a63ed1 100644 --- a/AssetsPickerViewController/Classes/Picker/AssetsCameraManager.swift +++ b/AssetsPickerViewController/Classes/Picker/AssetsCameraManager.swift @@ -8,6 +8,7 @@ import Foundation import AVFoundation import Photos +import UIKit protocol AssetsPickerManagerDelegate: NSObject { func assetsPickerManagerSavedAsset(identifier: String) diff --git a/AssetsPickerViewController/Classes/Picker/AssetsPickerConfig.swift b/AssetsPickerViewController/Classes/Picker/AssetsPickerConfig.swift index 5519cda..68f3e61 100644 --- a/AssetsPickerViewController/Classes/Picker/AssetsPickerConfig.swift +++ b/AssetsPickerViewController/Classes/Picker/AssetsPickerConfig.swift @@ -9,7 +9,8 @@ import UIKit import Photos -open class AssetsPickerConfig { +@objcMembers +open class AssetsPickerConfig : NSObject { // MARK: - Localized Strings Config @@ -93,6 +94,7 @@ open class AssetsPickerConfig { open var assetIsForcedSelectAssetFromCamera: Bool = true // MARK: Fetch + open var isVideoAllowed: Bool = false open var assetFetchOptions: [PHAssetCollectionType: PHFetchOptions]? // MARK: Custom Layout @@ -126,7 +128,7 @@ open class AssetsPickerConfig { return CGSize(width: edge, height: edge) } - public init() {} + public override init() {} @discardableResult open func prepare() -> Self { @@ -180,7 +182,11 @@ open class AssetsPickerConfig { NSSortDescriptor(key: "creationDate", ascending: true), NSSortDescriptor(key: "modificationDate", ascending: true) ] - options.predicate = NSPredicate(format: "mediaType = %d OR mediaType = %d", PHAssetMediaType.image.rawValue, PHAssetMediaType.video.rawValue) + options.predicate = NSPredicate(format: "mediaType = %d", PHAssetMediaType.image.rawValue) + if isVideoAllowed { + options.predicate = NSPredicate(format: "mediaType = %d OR mediaType = %d", PHAssetMediaType.image.rawValue, PHAssetMediaType.video.rawValue) + } + assetFetchOptions = [ .smartAlbum: options, .album: options, diff --git a/AssetsPickerViewController/Classes/Picker/Controller/AssetsPickerViewController.swift b/AssetsPickerViewController/Classes/Picker/Controller/AssetsPickerViewController.swift index 5717479..f87b0ba 100644 --- a/AssetsPickerViewController/Classes/Picker/Controller/AssetsPickerViewController.swift +++ b/AssetsPickerViewController/Classes/Picker/Controller/AssetsPickerViewController.swift @@ -22,6 +22,7 @@ import Photos } // MARK: - AssetsPickerViewController +@objcMembers open class AssetsPickerViewController: UINavigationController { @objc open weak var pickerDelegate: AssetsPickerViewControllerDelegate? @@ -68,7 +69,6 @@ open class AssetsPickerViewController: UINavigationController { controller.pickerConfig = config self.photoViewController = controller - AssetsManager.shared.registerObserver() viewControllers = [photoViewController] } diff --git a/AssetsPickerViewController/Classes/Utility/Device+AssetsPickerViewController.swift b/AssetsPickerViewController/Classes/Utility/Device+AssetsPickerViewController.swift deleted file mode 100644 index ea5cbe0..0000000 --- a/AssetsPickerViewController/Classes/Utility/Device+AssetsPickerViewController.swift +++ /dev/null @@ -1,20 +0,0 @@ -// -// Device+AssetsPickerViewController.swift -// AssetsPickerViewController -// -// Created by DragonCherry on 19/12/2017. -// - -import Device - -extension Device { - static func safeAreaInsets(isPortrait: Bool) -> UIEdgeInsets { - let size = Device.size() - switch size { - case .screen5_8Inch: - return isPortrait ? UIEdgeInsets(top: 88, left: 0, bottom: 34, right: 0) : UIEdgeInsets(top: 32, left: 44, bottom: 21, right: 44) - default: - return isPortrait ? UIEdgeInsets(top: 64, left: 0, bottom: 0, right: 0) : UIEdgeInsets(top: 30, left: 0, bottom: 0, right: 0) - } - } -} diff --git a/AssetsPickerViewController/Classes/Utility/String+AssetsPickerViewController.swift b/AssetsPickerViewController/Classes/Utility/String+AssetsPickerViewController.swift index 53fe3b7..71e21db 100644 --- a/AssetsPickerViewController/Classes/Utility/String+AssetsPickerViewController.swift +++ b/AssetsPickerViewController/Classes/Utility/String+AssetsPickerViewController.swift @@ -15,7 +15,11 @@ extension String { let customConfig = AssetsPickerConfig.customStringConfig, let localizedKey = AssetsPickerLocalizedStringKey(rawValue: key), let string = customConfig[localizedKey] else { +#if SWIFT_PACKAGE + self = Bundle.module.localizedString(forKey: key, value: key, table: "AssetsPickerViewController") +#else self = Bundle.assetsPickerBundle.localizedString(forKey: key, value: key, table: "AssetsPickerViewController") +#endif return } self = string diff --git a/AssetsPickerViewController/Classes/Utility/UIColor+AssetsPickerViewController.swift b/AssetsPickerViewController/Classes/Utility/UIColor+AssetsPickerViewController.swift index a18fcfb..d0436d4 100644 --- a/AssetsPickerViewController/Classes/Utility/UIColor+AssetsPickerViewController.swift +++ b/AssetsPickerViewController/Classes/Utility/UIColor+AssetsPickerViewController.swift @@ -6,6 +6,7 @@ // import Foundation +import UIKit extension UIColor { diff --git a/AssetsPickerViewController/Classes/Utility/UIScreen+AssetsPickerViewController.swift b/AssetsPickerViewController/Classes/Utility/UIScreen+AssetsPickerViewController.swift index e1dcd3b..0c4a9ec 100644 --- a/AssetsPickerViewController/Classes/Utility/UIScreen+AssetsPickerViewController.swift +++ b/AssetsPickerViewController/Classes/Utility/UIScreen+AssetsPickerViewController.swift @@ -7,9 +7,20 @@ // import UIKit -import Device extension UIScreen { + static func safeAreaInsets(isPortrait: Bool) -> UIEdgeInsets { + let w: Double = Double(UIScreen.main.bounds.width) + let h: Double = Double(UIScreen.main.bounds.height) + let screenHeight: Double = max(w, h) + + switch screenHeight { + case 812: // 5.8" (iPhone X/XS/XR/11) + return isPortrait ? UIEdgeInsets(top: 88, left: 0, bottom: 34, right: 0) : UIEdgeInsets(top: 32, left: 44, bottom: 21, right: 44) + default: + return isPortrait ? UIEdgeInsets(top: 64, left: 0, bottom: 0, right: 0) : UIEdgeInsets(top: 30, left: 0, bottom: 0, right: 0) + } + } var portraitSize: CGSize { let size = UIScreen.main.bounds.size @@ -24,8 +35,8 @@ extension UIScreen { var portraitContentSize: CGSize { var size = UIScreen.main.portraitSize if #available(iOS 11.0, *) { - size.width -= Device.safeAreaInsets(isPortrait: true).left + Device.safeAreaInsets(isPortrait: true).right - size.height -= Device.safeAreaInsets(isPortrait: true).top + Device.safeAreaInsets(isPortrait: true).bottom + size.width -= UIScreen.safeAreaInsets(isPortrait: true).left + UIScreen.safeAreaInsets(isPortrait: true).right + size.height -= UIScreen.safeAreaInsets(isPortrait: true).top + UIScreen.safeAreaInsets(isPortrait: true).bottom } return size } @@ -33,8 +44,8 @@ extension UIScreen { var landscapeContentSize: CGSize { var size = UIScreen.main.landscapeSize if #available(iOS 11.0, *) { - size.width -= Device.safeAreaInsets(isPortrait: false).left + Device.safeAreaInsets(isPortrait: false).right - size.height -= Device.safeAreaInsets(isPortrait: false).top + Device.safeAreaInsets(isPortrait: false).bottom + size.width -= UIScreen.safeAreaInsets(isPortrait: false).left + UIScreen.safeAreaInsets(isPortrait: false).right + size.height -= UIScreen.safeAreaInsets(isPortrait: false).top + UIScreen.safeAreaInsets(isPortrait: false).bottom } return size } diff --git a/Example/AssetsPickerViewController.xcodeproj/project.pbxproj b/Example/AssetsPickerViewController.xcodeproj/project.pbxproj index d060ebb..d976757 100644 --- a/Example/AssetsPickerViewController.xcodeproj/project.pbxproj +++ b/Example/AssetsPickerViewController.xcodeproj/project.pbxproj @@ -344,13 +344,11 @@ inputPaths = ( "${PODS_ROOT}/Target Support Files/Pods-AssetsPickerViewController_Example/Pods-AssetsPickerViewController_Example-frameworks.sh", "${BUILT_PRODUCTS_DIR}/AssetsPickerViewController/AssetsPickerViewController.framework", - "${BUILT_PRODUCTS_DIR}/Device/Device.framework", "${BUILT_PRODUCTS_DIR}/SnapKit/SnapKit.framework", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AssetsPickerViewController.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Device.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SnapKit.framework", ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Example/Podfile.lock b/Example/Podfile.lock index 2efd7c2..16aca5c 100644 --- a/Example/Podfile.lock +++ b/Example/Podfile.lock @@ -1,16 +1,13 @@ PODS: - - AssetsPickerViewController (2.9.2): - - Device + - AssetsPickerViewController (2.9.6): - SnapKit - - Device (3.1.2) - - SnapKit (5.0.0) + - SnapKit (5.0.1) DEPENDENCIES: - AssetsPickerViewController (from `../`) SPEC REPOS: - https://github.com/CocoaPods/Specs.git: - - Device + trunk: - SnapKit EXTERNAL SOURCES: @@ -18,9 +15,8 @@ EXTERNAL SOURCES: :path: "../" SPEC CHECKSUMS: - AssetsPickerViewController: 6626c9640d75ee4b60eea9b0fddf95cac0f41124 - Device: 62242076214c30fb5760174b3601cefafa70a481 - SnapKit: fd22d10eb9aff484d79a8724eab922c1ddf89bcf + AssetsPickerViewController: afad53707ec66ddd15182fa62630448736ed3976 + SnapKit: 97b92857e3df3a0c71833cce143274bf6ef8e5eb PODFILE CHECKSUM: ed535a80c7e9986ea173935163bbd69a1c717311 diff --git a/Package.swift b/Package.swift new file mode 100644 index 0000000..469d170 --- /dev/null +++ b/Package.swift @@ -0,0 +1,27 @@ +// swift-tools-version:5.3 +import PackageDescription + +let package = Package( + name: "AssetsPickerViewController", + defaultLocalization: "en", + platforms: [.iOS(.v10)], + products: [ + .library( + name: "AssetsPickerViewController", + targets: ["AssetsPickerViewController"] + ) + ], + dependencies: [ + .package(url: "https://github.com/SnapKit/SnapKit", from: "5.0.1") + ], + targets: [ + .target( + name: "AssetsPickerViewController", + dependencies: ["SnapKit"], + path: "AssetsPickerViewController", + sources: ["Classes"], + resources: [.process("Assets")] + ) + ], + swiftLanguageVersions: [.v4_2, .v5] +) diff --git a/README.md b/README.md index 35455fe..633a856 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -# Now iOS 14 supports multiple asset picker by default. I recommend use PHPickerViewController instead of this picker for many reasons. +# Now iOS 14 supports multiple asset picker by default. I recommend use PHPickerViewController from iOS 14 instead of this, it's a much better solution. ## AssetsPickerViewController @@ -101,10 +101,10 @@ Customizable Album & Asset Layout - multiple selection by dragging cells (from iOS 13) +- SPM(Swift Package Manager) support -## Features To-do -- SPM(Swift Package Manager) support +## Features To-do - Cropping image before select