Skip to content

Commit

Permalink
fetchResult: PHFetchResult<PHAsset> now accessed via getAsset to prev…
Browse files Browse the repository at this point in the history
…ent crash with index out of range.
  • Loading branch information
NikKovIos committed Jan 6, 2022
1 parent 9db1076 commit beed49d
Show file tree
Hide file tree
Showing 6 changed files with 98 additions and 52 deletions.
13 changes: 13 additions & 0 deletions Source/Helpers/Extensions/Array+Extensions.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//
// Array+Extensions.swift
// YPImagePicker
//
// Created by Nik Kov on 06.01.2022.
// Copyright © 2022 Yummypets. All rights reserved.
//

internal extension Array {
subscript (safe index: Index) -> Element? {
return indices.contains(index) ? self[index] : nil
}
}
21 changes: 18 additions & 3 deletions Source/Pages/Gallery/LibraryMediaManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class LibraryMediaManager {

weak var v: YPLibraryView?
var collection: PHAssetCollection?
internal var fetchResult: PHFetchResult<PHAsset>!
internal var fetchResult: PHFetchResult<PHAsset>?
internal var previousPreheatRect: CGRect = .zero
internal var imageManager: PHCachingImageManager?
internal var exportTimer: Timer?
Expand Down Expand Up @@ -60,8 +60,11 @@ class LibraryMediaManager {
addedIndexPaths += indexPaths
})

let assetsToStartCaching = fetchResult.assetsAtIndexPaths(addedIndexPaths)
let assetsToStopCaching = fetchResult.assetsAtIndexPaths(removedIndexPaths)
guard let assetsToStartCaching = fetchResult?.assetsAtIndexPaths(addedIndexPaths),
let assetsToStopCaching = fetchResult?.assetsAtIndexPaths(removedIndexPaths) else {
print("Some problems in fetching and caching assets.")
return
}

imageManager?.startCachingImages(for: assetsToStartCaching,
targetSize: cellSize,
Expand Down Expand Up @@ -215,4 +218,16 @@ class LibraryMediaManager {
s.cancelExport()
}
}

func getAsset(at index: Int) -> PHAsset? {
guard let fetchResult = fetchResult else {
print("FetchResult not contain this index: \(index)")
return nil
}
guard fetchResult.count > index else {
print("FetchResult not contain this index: \(index)")
return nil
}
return fetchResult.object(at: index)
}
}
8 changes: 4 additions & 4 deletions Source/Pages/Gallery/YPLibrary+LibraryChange.swift
Original file line number Diff line number Diff line change
Expand Up @@ -57,16 +57,16 @@ extension YPLibraryVC: PHPhotoLibraryChangeObserver {
// If no items selected in assetView, but there are already photos
// after photoLibraryDidChange, than select first item in library.
// It can be when user add photos from limited permission.
if self.mediaManager.fetchResult.count > 0,
selectedItems.isEmpty {
let newAsset = self.mediaManager.fetchResult[0]
if self.mediaManager.hasResultItems,
selectedItems.isEmpty,
let newAsset = self.mediaManager.getAsset(at: 0) {
self.changeAsset(newAsset)
}

// If user decided to forbid all photos with limited permission
// while using the lib we need to remove asset from assets view.
if selectedItems.isEmpty == false,
self.mediaManager.fetchResult.count == 0 {
self.mediaManager.hasResultItems == false {
self.v.assetZoomableView.clearAsset()
self.selectedItems.removeAll()
self.delegate?.libraryViewFinishedLoading()
Expand Down
41 changes: 23 additions & 18 deletions Source/Pages/Gallery/YPLibraryVC+CollectionView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ extension YPLibraryVC {

/// When tapping on the cell with long press, clear all previously selected cells.
@objc func handleLongPress(longPressGR: UILongPressGestureRecognizer) {
if multipleSelectionEnabled || isProcessing || YPConfig.library.maxNumberOfItems <= 1 {
if isMultipleSelectionEnabled || isProcessing || YPConfig.library.maxNumberOfItems <= 1 {
return
}

Expand All @@ -39,11 +39,11 @@ extension YPLibraryVC {

func startMultipleSelection(at indexPath: IndexPath) {
currentlySelectedIndex = indexPath.row
multipleSelectionButtonTapped()
toggleMultipleSelection()

// Update preview.
changeAsset(mediaManager.fetchResult[indexPath.row])
changeAsset(mediaManager.getAsset(at: indexPath.row))

// Bring preview down and keep selected cell visible.
panGestureHelper.resetToOriginalState()
if !panGestureHelper.isImageShown {
Expand All @@ -57,7 +57,7 @@ extension YPLibraryVC {
/// Removes cell from selection
func deselect(indexPath: IndexPath) {
if let positionIndex = selectedItems.firstIndex(where: {
$0.assetIdentifier == mediaManager.fetchResult[indexPath.row].localIdentifier
$0.assetIdentifier == mediaManager.getAsset(at: indexPath.row)?.localIdentifier
}) {
selectedItems.remove(at: positionIndex)

Expand All @@ -70,7 +70,7 @@ extension YPLibraryVC {
v.collectionView.deselectItem(at: indexPath, animated: false)
v.collectionView.selectItem(at: previouslySelectedIndexPath, animated: false, scrollPosition: [])
currentlySelectedIndex = previouslySelectedIndexPath.row
changeAsset(mediaManager.fetchResult[previouslySelectedIndexPath.row])
changeAsset(mediaManager.getAsset(at: previouslySelectedIndexPath.row))
}

checkLimit()
Expand All @@ -83,40 +83,45 @@ extension YPLibraryVC {
numSelections: selectedItems.count) ?? true) {
return
}

let asset = mediaManager.fetchResult[indexPath.item]
guard let asset = mediaManager.getAsset(at: indexPath.item) else {
print("No asset to add to selection.")
return
}

let newSelection = YPLibrarySelection(index: indexPath.row, assetIdentifier: asset.localIdentifier)
selectedItems.append(newSelection)
checkLimit()
}

func isInSelectionPool(indexPath: IndexPath) -> Bool {
return selectedItems.contains(where: {
$0.assetIdentifier == mediaManager.fetchResult[indexPath.row].localIdentifier
$0.assetIdentifier == mediaManager.getAsset(at: indexPath.row)?.localIdentifier
})
}

/// Checks if there can be selected more items. If no - present warning.
func checkLimit() {
v.maxNumberWarningView.isHidden = !isLimitExceeded || multipleSelectionEnabled == false
v.maxNumberWarningView.isHidden = !isLimitExceeded || isMultipleSelectionEnabled == false
}
}

extension YPLibraryVC: UICollectionViewDataSource {
public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return mediaManager.fetchResult.count
return mediaManager.fetchResult?.count ?? 0
}
}

extension YPLibraryVC: UICollectionViewDelegate {

public func collectionView(_ collectionView: UICollectionView,
cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let asset = mediaManager.fetchResult[indexPath.item]
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "YPLibraryViewCell",
for: indexPath) as? YPLibraryViewCell else {
fatalError("unexpected cell in collection view")
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "YPLibraryViewCell", for: indexPath) as? YPLibraryViewCell else {
fatalError("unexpected cell in collection view")
}
guard let asset = mediaManager.getAsset(at: indexPath.item) else {
return cell
}

cell.representedAssetIdentifier = asset.localIdentifier
cell.multipleSelectionIndicator.selectionColor =
YPConfig.colors.multipleItemsSelectedCircleColor ?? YPConfig.colors.tintColor
Expand All @@ -134,7 +139,7 @@ extension YPLibraryVC: UICollectionViewDelegate {
let isVideo = (asset.mediaType == .video)
cell.durationLabel.isHidden = !isVideo
cell.durationLabel.text = isVideo ? YPHelper.formattedStrigFrom(asset.duration) : ""
cell.multipleSelectionIndicator.isHidden = !multipleSelectionEnabled
cell.multipleSelectionIndicator.isHidden = !isMultipleSelectionEnabled
cell.isSelected = currentlySelectedIndex == indexPath.row

// Set correct selection number
Expand Down Expand Up @@ -163,7 +168,7 @@ extension YPLibraryVC: UICollectionViewDelegate {
let previouslySelectedIndexPath = IndexPath(row: currentlySelectedIndex, section: 0)
currentlySelectedIndex = indexPath.row

changeAsset(mediaManager.fetchResult[indexPath.row])
changeAsset(mediaManager.getAsset(at: indexPath.row))
panGestureHelper.resetToOriginalState()

// Only scroll cell to top if preview is hidden.
Expand All @@ -172,7 +177,7 @@ extension YPLibraryVC: UICollectionViewDelegate {
}
v.refreshImageCurtainAlpha()

if multipleSelectionEnabled {
if isMultipleSelectionEnabled {
let cellIsInTheSelectionPool = isInSelectionPool(indexPath: indexPath)
let cellIsCurrentlySelected = previouslySelectedIndexPath.row == currentlySelectedIndex
if cellIsInTheSelectionPool {
Expand Down
61 changes: 34 additions & 27 deletions Source/Pages/Gallery/YPLibraryVC.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ internal final class YPLibraryVC: UIViewController, YPPermissionCheckable {
internal var isProcessing = false // true if video or image is in processing state
internal var selectedItems = [YPLibrarySelection]()
internal let mediaManager = LibraryMediaManager()
internal var multipleSelectionEnabled = false
internal var isMultipleSelectionEnabled = false
internal var currentlySelectedIndex: Int = 0
internal let panGestureHelper = PanGestureHelper()
internal var isInitialized = false
Expand Down Expand Up @@ -75,7 +75,7 @@ internal final class YPLibraryVC: UIViewController, YPPermissionCheckable {
// The negative index will be corrected in the collectionView:cellForItemAt:
return YPLibrarySelection(index: -1, assetIdentifier: asset.localIdentifier)
}
v.assetViewContainer.setMultipleSelectionMode(on: multipleSelectionEnabled)
v.assetViewContainer.setMultipleSelectionMode(on: isMultipleSelectionEnabled)
v.collectionView.reloadData()
}

Expand All @@ -84,15 +84,15 @@ internal final class YPLibraryVC: UIViewController, YPPermissionCheckable {
}

if YPConfig.library.defaultMultipleSelection || selectedItems.count > 1 {
showMultipleSelection()
toggleMultipleSelection()
}
}

func setAlbum(_ album: YPAlbum) {
title = album.title
mediaManager.collection = album.collection
currentlySelectedIndex = 0
if !multipleSelectionEnabled {
if !isMultipleSelectionEnabled {
selectedItems.removeAll()
}
refreshMediaRequest()
Expand Down Expand Up @@ -171,27 +171,28 @@ internal final class YPLibraryVC: UIViewController, YPPermissionCheckable {
}

doAfterLibraryPermissionCheck { [weak self] in
if self?.multipleSelectionEnabled == false {
if self?.isMultipleSelectionEnabled == false {
self?.selectedItems.removeAll()
}
self?.showMultipleSelection()
self?.toggleMultipleSelection()
}
}

func showMultipleSelection() {
func toggleMultipleSelection() {
// Prevent desactivating multiple selection when using `minNumberOfItems`
if YPConfig.library.minNumberOfItems > 1 && multipleSelectionEnabled {
print("Selected minNumberOfItems greater than one :\(YPConfig.library.minNumberOfItems). Don't deselect multiple selection.")
if YPConfig.library.minNumberOfItems > 1 && isMultipleSelectionEnabled {
print("Selected minNumberOfItems greater than one :\(YPConfig.library.minNumberOfItems). Don't deselecting multiple selection.")
return
}

multipleSelectionEnabled = !multipleSelectionEnabled

if multipleSelectionEnabled {
if selectedItems.isEmpty && YPConfig.library.preSelectItemOnMultipleSelection,
delegate?.libraryViewShouldAddToSelection(indexPath: IndexPath(row: currentlySelectedIndex, section: 0),
numSelections: selectedItems.count) ?? true {
let asset = mediaManager.fetchResult[currentlySelectedIndex]

isMultipleSelectionEnabled.toggle()

if isMultipleSelectionEnabled {
let needPreselectItemsAndNotSelectedAnyYet = selectedItems.isEmpty && YPConfig.library.preSelectItemOnMultipleSelection
let shouldSelectByDelegate = delegate?.libraryViewShouldAddToSelection(indexPath: IndexPath(row: currentlySelectedIndex, section: 0), numSelections: selectedItems.count) ?? true
if needPreselectItemsAndNotSelectedAnyYet,
shouldSelectByDelegate,
let asset = mediaManager.getAsset(at: currentlySelectedIndex) {
selectedItems = [
YPLibrarySelection(index: currentlySelectedIndex,
cropRect: v.currentCropRect(),
Expand All @@ -205,10 +206,10 @@ internal final class YPLibraryVC: UIViewController, YPPermissionCheckable {
addToSelection(indexPath: IndexPath(row: currentlySelectedIndex, section: 0))
}

v.assetViewContainer.setMultipleSelectionMode(on: multipleSelectionEnabled)
v.assetViewContainer.setMultipleSelectionMode(on: isMultipleSelectionEnabled)
v.collectionView.reloadData()
checkLimit()
delegate?.libraryViewDidToggleMultipleSelection(enabled: multipleSelectionEnabled)
delegate?.libraryViewDidToggleMultipleSelection(enabled: isMultipleSelectionEnabled)
}

// MARK: - Tap Preview
Expand Down Expand Up @@ -236,13 +237,14 @@ internal final class YPLibraryVC: UIViewController, YPPermissionCheckable {
mediaManager.fetchResult = PHAsset.fetchAssets(with: options)
}

if mediaManager.hasResultItems {
changeAsset(mediaManager.fetchResult[0])
if mediaManager.hasResultItems,
let firstAsset = mediaManager.getAsset(at: 0) {
changeAsset(firstAsset)
v.collectionView.reloadData()
v.collectionView.selectItem(at: IndexPath(row: 0, section: 0),
animated: false,
scrollPosition: UICollectionView.ScrollPosition())
if !multipleSelectionEnabled && YPConfig.library.preSelectItemOnMultipleSelection {
animated: false,
scrollPosition: UICollectionView.ScrollPosition())
if !isMultipleSelectionEnabled && YPConfig.library.preSelectItemOnMultipleSelection {
addToSelection(indexPath: IndexPath(row: 0, section: 0))
}
} else {
Expand Down Expand Up @@ -277,7 +279,12 @@ internal final class YPLibraryVC: UIViewController, YPPermissionCheckable {
}
}

func changeAsset(_ asset: PHAsset) {
func changeAsset(_ asset: PHAsset?) {
guard let asset = asset else {
print("No asset to change.")
return
}

delegate?.libraryViewStartedLoadingImage()

let completion = { (isLowResIntermediaryImage: Bool) in
Expand Down Expand Up @@ -361,7 +368,7 @@ internal final class YPLibraryVC: UIViewController, YPPermissionCheckable {
}

internal func fetchStoredCrop() -> YPLibrarySelection? {
if self.multipleSelectionEnabled,
if self.isMultipleSelectionEnabled,
self.selectedItems.contains(where: { $0.index == self.currentlySelectedIndex }) {
guard let selectedAssetIndex = self.selectedItems
.firstIndex(where: { $0.index == self.currentlySelectedIndex }) else {
Expand Down Expand Up @@ -441,7 +448,7 @@ internal final class YPLibraryVC: UIViewController, YPPermissionCheckable {
}

// Multiple selection
if self.multipleSelectionEnabled && self.selectedItems.count > 1 {
if self.isMultipleSelectionEnabled && self.selectedItems.count > 1 {

// Check video length
for asset in selectedAssets {
Expand Down
6 changes: 6 additions & 0 deletions YPImagePicker.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,8 @@
EBD2B641207B7D7400E711C2 /* YPMediaItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBD2B640207B7D7400E711C2 /* YPMediaItem.swift */; };
EBD66105208104EA00EA276E /* YPIcons.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBD66104208104EA00EA276E /* YPIcons.swift */; };
EBD66107208104F400EA276E /* YPColors.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBD66106208104F400EA276E /* YPColors.swift */; };
EBDDF0EB278784A900A372FD /* Array+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBDDF0EA278784A900A372FD /* Array+Extensions.swift */; };
EBDDF0EC278784A900A372FD /* Array+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBDDF0EA278784A900A372FD /* Array+Extensions.swift */; };
EBE6CA48210389ED005B0A6A /* CIImage+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBE6CA47210389ED005B0A6A /* CIImage+Extensions.swift */; };
/* End PBXBuildFile section */

Expand Down Expand Up @@ -334,6 +336,7 @@
EBD66104208104EA00EA276E /* YPIcons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YPIcons.swift; sourceTree = "<group>"; };
EBD66106208104F400EA276E /* YPColors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YPColors.swift; sourceTree = "<group>"; };
EBDDF0E62787069E00A372FD /* Package.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Package.swift; sourceTree = "<group>"; };
EBDDF0EA278784A900A372FD /* Array+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+Extensions.swift"; sourceTree = "<group>"; };
EBE6CA47210389ED005B0A6A /* CIImage+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CIImage+Extensions.swift"; sourceTree = "<group>"; };
/* End PBXFileReference section */

Expand Down Expand Up @@ -553,6 +556,7 @@
321E6D86240CFABC00D76CD8 /* UINavigationBar+Extensions.swift */,
EB473E16208E192800D16105 /* URL+Extensions.swift */,
EBA37BC826F757FF005DAAD4 /* Bundle+Extensions.swift */,
EBDDF0EA278784A900A372FD /* Array+Extensions.swift */,
);
path = Extensions;
sourceTree = "<group>";
Expand Down Expand Up @@ -937,6 +941,7 @@
99C6D6C91F1FB5C100711DB2 /* YPBottomPager.swift in Sources */,
EBA37BBC26F74CE0005DAAD4 /* YPPhotoFiltersVC.swift in Sources */,
99CF6D2B201CB96700487F77 /* YPVideoCaptureHelper.swift in Sources */,
EBDDF0EC278784A900A372FD /* Array+Extensions.swift in Sources */,
EBA37AFA26F7300C005DAAD4 /* YPPermissionManager.swift in Sources */,
99019E4E2018CD31007325C2 /* YPPagerMenu.swift in Sources */,
99C6D6C01F1FB5C100711DB2 /* YPAssetZoomableView.swift in Sources */,
Expand Down Expand Up @@ -983,6 +988,7 @@
EBA37B9826F74CBA005DAAD4 /* YPVideoProcessor.swift in Sources */,
EBA37B8526F74CBA005DAAD4 /* YPLibraryVC.swift in Sources */,
EBA37B9E26F74CBA005DAAD4 /* YPDragDirection.swift in Sources */,
EBDDF0EB278784A900A372FD /* Array+Extensions.swift in Sources */,
EBA37B5226F749C6005DAAD4 /* AppDelegate.swift in Sources */,
EBA37BB126F74CBA005DAAD4 /* YPLoaders.swift in Sources */,
EBA37B7526F74CBA005DAAD4 /* YPLibraryVC+PanGesture.swift in Sources */,
Expand Down

0 comments on commit beed49d

Please sign in to comment.