Skip to content

Commit e776878

Browse files
authored
Fix handling of deleted photos on iOS (#1556)
* Fix crash on iOS when images are deleted Fixes a crash on iOS when a displayed image is deleted in the background: NSInternalInconsistencyException reason: 'attempt to delete and reload the same index path <snip>' According to https://developer.apple.com/documentation/photokit/phfetchresultchangedetails?language=objc ...changedIndexes can't be used safely inside performBatchUpdates:completion: Instead, use changedIndexes after and outside the performBatchUpdates:completion: call... * Update selection when assets are removed from collection If a selected photo is deleted from the photo library, it needs to be removed from `selectedAssets` or the UI will hang when the user taps "Done". Also, the UI needs to be updated to reflect the new selection. * Update counts in footer when collection changes When the number of photos or videos in the collection changes, update the footer to reflect the new counts.
1 parent 415834e commit e776878

File tree

1 file changed

+89
-44
lines changed

1 file changed

+89
-44
lines changed

ios/QBImagePicker/QBImagePicker/QBAssetsViewController.m

Lines changed: 89 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -417,15 +417,31 @@ - (void)photoLibraryDidChange:(PHChange *)changeInstance
417417
if ([insertedIndexes count]) {
418418
[self.collectionView insertItemsAtIndexPaths:[insertedIndexes qb_indexPathsFromIndexesWithSection:0]];
419419
}
420-
421-
NSIndexSet *changedIndexes = [collectionChanges changedIndexes];
422-
if ([changedIndexes count]) {
423-
[self.collectionView reloadItemsAtIndexPaths:[changedIndexes qb_indexPathsFromIndexesWithSection:0]];
424-
}
425420
} completion:NULL];
421+
422+
NSIndexSet *changedIndexes = [collectionChanges changedIndexes];
423+
if ([changedIndexes count]) {
424+
[self.collectionView reloadItemsAtIndexPaths:[changedIndexes qb_indexPathsFromIndexesWithSection:0]];
425+
}
426426
}
427427

428428
[self resetCachedAssets];
429+
430+
// Update the selection to remove any assets that have been removed from the collection
431+
NSMutableSet *removedAssets = [NSMutableSet new];
432+
for (PHAsset *asset in self.imagePickerController.selectedAssets) {
433+
if(![self.fetchResult containsObject:asset]) {
434+
[removedAssets addObject:asset];
435+
}
436+
}
437+
[self removeAssetsFromSelection:removedAssets];
438+
439+
// Update the footer to show the current photo/video counts
440+
NSArray<UICollectionReusableView *> *footers =
441+
[self.collectionView visibleSupplementaryViewsOfKind:UICollectionElementKindSectionFooter];
442+
if (footers.count) {
443+
[self updateFooterView:footers[0]];
444+
}
429445
}
430446
});
431447
}
@@ -507,57 +523,60 @@ - (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView
507523
UICollectionReusableView *footerView = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionFooter
508524
withReuseIdentifier:@"FooterView"
509525
forIndexPath:indexPath];
526+
[self updateFooterView:footerView];
510527

511-
// Number of assets
512-
UILabel *label = (UILabel *)[footerView viewWithTag:1];
528+
return footerView;
529+
}
513530

514-
NSBundle *bundle = self.imagePickerController.assetBundle;
515-
NSUInteger numberOfPhotos = [self.fetchResult countOfAssetsWithMediaType:PHAssetMediaTypeImage];
516-
NSUInteger numberOfVideos = [self.fetchResult countOfAssetsWithMediaType:PHAssetMediaTypeVideo];
531+
return nil;
532+
}
517533

518-
switch (self.imagePickerController.mediaType) {
519-
case QBImagePickerMediaTypeAny:
520-
{
521-
NSString *format;
522-
if (numberOfPhotos == 1) {
523-
if (numberOfVideos == 1) {
524-
format = NSLocalizedStringFromTableInBundle(@"assets.footer.photo-and-video", @"QBImagePicker", bundle, nil);
525-
} else {
526-
format = NSLocalizedStringFromTableInBundle(@"assets.footer.photo-and-videos", @"QBImagePicker", bundle, nil);
527-
}
528-
} else if (numberOfVideos == 1) {
529-
format = NSLocalizedStringFromTableInBundle(@"assets.footer.photos-and-video", @"QBImagePicker", bundle, nil);
534+
- (void)updateFooterView:(UICollectionReusableView *)footerView {
535+
// Number of assets
536+
UILabel *label = (UILabel *)[footerView viewWithTag:1];
537+
538+
NSBundle *bundle = self.imagePickerController.assetBundle;
539+
NSUInteger numberOfPhotos = [self.fetchResult countOfAssetsWithMediaType:PHAssetMediaTypeImage];
540+
NSUInteger numberOfVideos = [self.fetchResult countOfAssetsWithMediaType:PHAssetMediaTypeVideo];
541+
542+
switch (self.imagePickerController.mediaType) {
543+
case QBImagePickerMediaTypeAny:
544+
{
545+
NSString *format;
546+
if (numberOfPhotos == 1) {
547+
if (numberOfVideos == 1) {
548+
format = NSLocalizedStringFromTableInBundle(@"assets.footer.photo-and-video", @"QBImagePicker", bundle, nil);
530549
} else {
531-
format = NSLocalizedStringFromTableInBundle(@"assets.footer.photos-and-videos", @"QBImagePicker", bundle, nil);
550+
format = NSLocalizedStringFromTableInBundle(@"assets.footer.photo-and-videos", @"QBImagePicker", bundle, nil);
532551
}
533-
534-
label.text = [NSString stringWithFormat:format, numberOfPhotos, numberOfVideos];
552+
} else if (numberOfVideos == 1) {
553+
format = NSLocalizedStringFromTableInBundle(@"assets.footer.photos-and-video", @"QBImagePicker", bundle, nil);
554+
} else {
555+
format = NSLocalizedStringFromTableInBundle(@"assets.footer.photos-and-videos", @"QBImagePicker", bundle, nil);
535556
}
536-
break;
537557

538-
case QBImagePickerMediaTypeImage:
539-
{
540-
NSString *key = (numberOfPhotos == 1) ? @"assets.footer.photo" : @"assets.footer.photos";
541-
NSString *format = NSLocalizedStringFromTableInBundle(key, @"QBImagePicker", bundle, nil);
542-
543-
label.text = [NSString stringWithFormat:format, numberOfPhotos];
544-
}
545-
break;
558+
label.text = [NSString stringWithFormat:format, numberOfPhotos, numberOfVideos];
559+
}
560+
break;
546561

547-
case QBImagePickerMediaTypeVideo:
548-
{
549-
NSString *key = (numberOfVideos == 1) ? @"assets.footer.video" : @"assets.footer.videos";
550-
NSString *format = NSLocalizedStringFromTableInBundle(key, @"QBImagePicker", bundle, nil);
562+
case QBImagePickerMediaTypeImage:
563+
{
564+
NSString *key = (numberOfPhotos == 1) ? @"assets.footer.photo" : @"assets.footer.photos";
565+
NSString *format = NSLocalizedStringFromTableInBundle(key, @"QBImagePicker", bundle, nil);
551566

552-
label.text = [NSString stringWithFormat:format, numberOfVideos];
553-
}
554-
break;
567+
label.text = [NSString stringWithFormat:format, numberOfPhotos];
555568
}
569+
break;
556570

557-
return footerView;
558-
}
571+
case QBImagePickerMediaTypeVideo:
572+
{
573+
NSString *key = (numberOfVideos == 1) ? @"assets.footer.video" : @"assets.footer.videos";
574+
NSString *format = NSLocalizedStringFromTableInBundle(key, @"QBImagePicker", bundle, nil);
559575

560-
return nil;
576+
label.text = [NSString stringWithFormat:format, numberOfVideos];
577+
}
578+
break;
579+
}
561580
}
562581

563582

@@ -653,6 +672,32 @@ - (void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndex
653672
}
654673
}
655674

675+
- (void)removeAssetsFromSelection:(NSSet *)assets
676+
{
677+
if (assets.count == 0) {
678+
return;
679+
}
680+
681+
QBImagePickerController *imagePickerController = self.imagePickerController;
682+
NSMutableOrderedSet *selectedAssets = imagePickerController.selectedAssets;
683+
684+
// Remove assets from set
685+
[selectedAssets minusSet:assets];
686+
687+
self.lastSelectedItemIndexPath = nil;
688+
689+
[self updateDoneButtonState];
690+
691+
if (self.imagePickerController.showsNumberOfSelectedAssets) {
692+
[self updateSelectionInfo];
693+
694+
if (selectedAssets.count == 0) {
695+
// Hide toolbar
696+
[self.navigationController setToolbarHidden:YES animated:YES];
697+
}
698+
}
699+
}
700+
656701

657702
#pragma mark - UICollectionViewDelegateFlowLayout
658703

0 commit comments

Comments
 (0)