//Copyright (c) 2015 Katsuma Tanaka //Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to //deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or //sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: //The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. //THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, //FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER //LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS //IN THE SOFTWARE. #import "UGCKitAssetsViewController.h" #import #import "UGCKitAssetLoader.h" #import "UGCKitAlbumsViewController.h" #import "UGCKitImageScrollerViewController.h" // Views #import "UGCKitMediaPickerViewControllerPrivate.h" #import "UGCKitAssetCell.h" #import "UGCKitVideoIndicatorView.h" #import "UGCKitAssetLoadingController.h" #import "UGCKitMem.h" static const CGFloat DefaultSelectionHeight = 110; static CGSize CGSizeScale(CGSize size, CGFloat scale) { return CGSizeMake(size.width * scale, size.height * scale); } @interface UGCKitMediaPickerViewController (Private) @property (nonatomic, strong) NSBundle *assetBundle; @property (nonatomic, weak) UICollectionViewLayout *collectionViewLayout; @end @implementation NSIndexSet (Convenience) - (NSArray *)qb_indexPathsFromIndexesWithSection:(NSUInteger)section { NSMutableArray *indexPaths = [NSMutableArray arrayWithCapacity:self.count]; [self enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) { [indexPaths addObject:[NSIndexPath indexPathForItem:idx inSection:section]]; }]; return indexPaths; } @end @implementation UICollectionView (Convenience) - (NSArray *)qb_indexPathsForElementsInRect:(CGRect)rect { NSArray *allLayoutAttributes = [self.collectionViewLayout layoutAttributesForElementsInRect:rect]; if (allLayoutAttributes.count == 0) { return nil; } NSMutableArray *indexPaths = [NSMutableArray arrayWithCapacity:allLayoutAttributes.count]; for (UICollectionViewLayoutAttributes *layoutAttributes in allLayoutAttributes) { NSIndexPath *indexPath = layoutAttributes.indexPath; [indexPaths addObject:indexPath]; } return indexPaths; } @end @interface UGCKitAssetsViewController () @property (nonatomic, strong) UGCKitAssetLoader *loader; @property (nonatomic, copy) NSArray *exportedAssets; @property (nonatomic, strong) PHFetchResult *fetchResult; @property (nonatomic, strong) NSArray *assetCollections; @property (nonatomic, strong) PHCachingImageManager *imageManager; @property (nonatomic, assign) CGRect previousPreheatRect; @property (nonatomic, assign) BOOL disableScrollToBottom; @property (nonatomic, strong) NSIndexPath *lastSelectedItemIndexPath; @property (nonatomic, strong) UGCKitImageScrollerViewController *imagesController; @end @implementation UGCKitAssetsViewController - (void)viewDidLoad { [super viewDidLoad]; _loader = [[UGCKitAssetLoader alloc] init]; __weak __typeof(self) wself = self; _descriptionLabel.textColor = _theme.titleColor; [_loader load:^{ __strong __typeof(wself) self = wself; if (nil == self) return; self->_assetCollections = self->_loader.assetCollections; [self _updateWithAssetCollection:self->_assetCollections.firstObject]; }]; [self.doneButton setBackgroundImage:_theme.nextIcon forState:UIControlStateNormal]; [self.doneButton setTitle:[_theme localizedString:@"UGCKit.Common.Next"] forState:UIControlStateNormal]; [self.doneButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; UIButton *button = (UIButton *)self.navigationItem.titleView; [button setTitleColor:_theme.titleColor forState:UIControlStateNormal]; self.view.backgroundColor = _theme.backgroundColor; self.collectionView.backgroundColor = _theme.backgroundColor; [self setUpBottomView]; [self resetCachedAssets]; // Register observer [[PHPhotoLibrary sharedPhotoLibrary] registerChangeObserver:self]; } - (void)setTheme:(UGCKitTheme *)theme { _theme = theme; _descriptionLabel.textColor = _theme.titleColor; _timeLabel.textColor = _theme.titleColor; } - (UICollectionViewLayout *)collectionViewLayout { return self.collectionView.collectionViewLayout; } - (void)_updateWithAssetCollection:(PHAssetCollection *)assetCollection { self.assetCollection = assetCollection; UIButton *button = (UIButton *)self.navigationItem.titleView; [button setTitle:assetCollection.localizedTitle forState:UIControlStateNormal]; button.titleLabel.font = [UIFont systemFontOfSize:17]; [button sizeToFit]; self.navigationItem.titleView = button; [self.navigationItem.titleView layoutIfNeeded]; [self.collectionView reloadData]; } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; // Configure navigation item self.navigationItem.title = self.assetCollection.localizedTitle; self.navigationItem.prompt = self.imagePickerController .config.prompt; self.navigationController.navigationBar.hidden = NO; // Configure collection view self.collectionView.allowsMultipleSelection = self.imagePickerController.allowsMultipleSelection; // Show/hide 'Done' button self.doneButton.hidden = !self.imagePickerController.allowsMultipleSelection; [self updateDoneButtonState]; [self updateSelectionInfo]; [self.collectionView reloadData]; // Scroll to bottom if (self.fetchResult.count > 0 && self.isMovingToParentViewController && !self.disableScrollToBottom) { NSIndexPath *indexPath = [NSIndexPath indexPathForItem:(self.fetchResult.count - 1) inSection:0]; [self.collectionView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionTop animated:NO]; } } - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; self.disableScrollToBottom = YES; } - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; self.disableScrollToBottom = NO; [self updateCachedAssets]; _timeLabel.textColor = _theme.titleColor; _descriptionLabel.textColor = _theme.titleColor; } - (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id)coordinator { // Save indexPath for the last item NSIndexPath *indexPath = [[self.collectionView indexPathsForVisibleItems] lastObject]; // Update layout [self.collectionViewLayout invalidateLayout]; // Restore scroll position [coordinator animateAlongsideTransition:nil completion:^(id context) { [self.collectionView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionBottom animated:NO]; }]; } - (void)dealloc { // Deregister observer [[PHPhotoLibrary sharedPhotoLibrary] unregisterChangeObserver:self]; } #pragma mark - Accessors - (void)setAssetCollection:(PHAssetCollection *)assetCollection { _assetCollection = assetCollection; [self updateFetchRequest]; [self.collectionView reloadData]; } - (PHCachingImageManager *)imageManager { if (_imageManager == nil) { _imageManager = [PHCachingImageManager new]; } return _imageManager; } - (BOOL)isAutoDeselectEnabled { return (self.imagePickerController.maximumNumberOfSelection == 1 && self.imagePickerController.maximumNumberOfSelection >= self.imagePickerController.minimumNumberOfSelection); } #pragma mark - Delegate Methods - (void)tellDelegateWithAssets:(NSArray *)assets { UGCKitAssetLoadingController *loadvc = [[UGCKitAssetLoadingController alloc] initWithTheme:_theme]; WEAKIFY(self); WEAKIFY(loadvc); loadvc.completion = ^(UGCKitResult *result) { STRONGIFY(self); self.exportedAssets = weak_loadvc.avAssets; if (self.imagePickerController.completion) { self.imagePickerController.completion(result); } }; loadvc.combineVideos = self.imagePickerController.config.combineVideos; UGCKitMediaType mediaType = self.imagePickerController.mediaType; if (mediaType == UGCKitMediaTypeVideo) { [loadvc exportAssetList:assets assetType:AssetType_Video]; }else{ [loadvc exportAssetList:assets assetType:AssetType_Image]; } [self.navigationController pushViewController:loadvc animated:YES]; } #pragma mark - Actions - (IBAction)done:(id)sender { [self tellDelegateWithAssets:self.imagePickerController.selectedAssets.array]; // if ([self.imagePickerController.delegate respondsToSelector:@selector(qb_imagePickerController:didFinishPickingAssets:)]) { // [self.imagePickerController.delegate qb_imagePickerController:self.imagePickerController // didFinishPickingAssets:self.imagePickerController.selectedAssets.array]; // } } - (IBAction)cancel:(id)sender { if (self.imagePickerController.completion) { self.imagePickerController.completion([UGCKitResult cancelledResult]); } } #pragma mark - Toolbar - (void)setUpBottomView { self.imagesController = [[UGCKitImageScrollerViewController alloc] initWithImageManage:self.imageManager]; self.imagesController.closeIcon = _theme.closeIcon; __weak __typeof(self) wself = self; self.imagesController.onRemoveHandler = ^(NSUInteger index) { PHAsset *removedAsset = wself.imagePickerController.selectedAssets[index]; NSUInteger indexOfCollection = [wself.fetchResult indexOfObject:removedAsset]; NSMutableOrderedSet *selectedAssets = wself.imagePickerController.selectedAssets; [selectedAssets removeObjectAtIndex:index]; [wself updateSelectionInfo]; [wself.collectionView deselectItemAtIndexPath:[NSIndexPath indexPathForItem:indexOfCollection inSection:0] animated:YES]; }; self.imagesController.collectionView.backgroundColor = self.view.backgroundColor; [self.view insertSubview:self.imagesController.view atIndex:0]; CGFloat offset = 0; if (@available(iOS 11.0, *)) { offset = [UIApplication sharedApplication].keyWindow.safeAreaInsets.bottom; } self.imagesController.view.frame = CGRectMake(0, CGRectGetMaxY(self.view.bounds) - DefaultSelectionHeight - offset, CGRectGetWidth(self.view.bounds), DefaultSelectionHeight); self.imagesController.view.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleWidth; } - (void)updateSelectionInfo { NSMutableOrderedSet *selectedAssets = self.imagePickerController.selectedAssets; long duration = 0; for (PHAsset *asset in selectedAssets) { duration += asset.duration; } long minutes = (long)(duration / 60.0); int seconds = (int)ceil(duration - 60.0 * (double)minutes); self.timeLabel.text = [NSString stringWithFormat:@"%02ld:%02d", minutes, seconds]; } #pragma mark - Fetching Assets - (void)updateFetchRequest { if (self.assetCollection) { PHFetchOptions *options = [PHFetchOptions new]; switch (self.imagePickerController.mediaType) { case UGCKitMediaTypePhoto: options.predicate = [NSPredicate predicateWithFormat:@"mediaType == %ld", PHAssetMediaTypeImage]; break; case UGCKitMediaTypeVideo: options.predicate = [NSPredicate predicateWithFormat:@"mediaType == %ld", PHAssetMediaTypeVideo]; break; default: break; } self.fetchResult = [PHAsset fetchAssetsInAssetCollection:self.assetCollection options:options]; if ([self isAutoDeselectEnabled] && self.imagePickerController.selectedAssets.count > 0) { // Get index of previous selected asset PHAsset *asset = [self.imagePickerController.selectedAssets firstObject]; NSInteger assetIndex = [self.fetchResult indexOfObject:asset]; self.lastSelectedItemIndexPath = [NSIndexPath indexPathForItem:assetIndex inSection:0]; } } else { self.fetchResult = nil; } } #pragma mark - Checking for Selection Limit - (BOOL)isMinimumSelectionLimitFulfilled { return (self.imagePickerController.minimumNumberOfSelection <= self.imagePickerController.selectedAssets.count); } - (BOOL)isMaximumSelectionLimitReached { NSUInteger minimumNumberOfSelection = MAX(1, self.imagePickerController.minimumNumberOfSelection); if (minimumNumberOfSelection <= self.imagePickerController.maximumNumberOfSelection) { return (self.imagePickerController.maximumNumberOfSelection <= self.imagePickerController.selectedAssets.count); } return NO; } - (void)updateDoneButtonState { self.doneButton.enabled = [self isMinimumSelectionLimitFulfilled]; } #pragma mark - Segue - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { UINavigationController *navigationController = (UINavigationController *)segue.destinationViewController; UGCKitAlbumsViewController *controller = (UGCKitAlbumsViewController *)navigationController.viewControllers.firstObject; controller.imagePickerController = self.imagePickerController; controller.assetCollections = self.assetCollections; __weak __typeof(self) wself = self; controller.onPickAssetCollection = ^(PHAssetCollection *collection) { [self _updateWithAssetCollection:collection]; [wself dismissViewControllerAnimated:YES completion:nil]; }; } #pragma mark - Asset Caching - (void)resetCachedAssets { [self.imageManager stopCachingImagesForAllAssets]; self.previousPreheatRect = CGRectZero; } - (void)updateCachedAssets { BOOL isViewVisible = [self isViewLoaded] && self.view.window != nil; if (!isViewVisible) { return; } // The preheat window is twice the height of the visible rect CGRect preheatRect = self.collectionView.bounds; preheatRect = CGRectInset(preheatRect, 0.0, -0.5 * CGRectGetHeight(preheatRect)); // If scrolled by a "reasonable" amount... CGFloat delta = ABS(CGRectGetMidY(preheatRect) - CGRectGetMidY(self.previousPreheatRect)); if (delta > CGRectGetHeight(self.collectionView.bounds) / 3.0) { // Compute the assets to start caching and to stop caching NSMutableArray *addedIndexPaths = [NSMutableArray array]; NSMutableArray *removedIndexPaths = [NSMutableArray array]; [self computeDifferenceBetweenRect:self.previousPreheatRect andRect:preheatRect addedHandler:^(CGRect addedRect) { NSArray *indexPaths = [self.collectionView qb_indexPathsForElementsInRect:addedRect]; [addedIndexPaths addObjectsFromArray:indexPaths]; } removedHandler:^(CGRect removedRect) { NSArray *indexPaths = [self.collectionView qb_indexPathsForElementsInRect:removedRect]; [removedIndexPaths addObjectsFromArray:indexPaths]; }]; NSArray *assetsToStartCaching = [self assetsAtIndexPaths:addedIndexPaths]; NSArray *assetsToStopCaching = [self assetsAtIndexPaths:removedIndexPaths]; CGSize itemSize = [(UICollectionViewFlowLayout *)self.collectionViewLayout itemSize]; CGSize targetSize = CGSizeScale(itemSize, self.traitCollection.displayScale); [self.imageManager startCachingImagesForAssets:assetsToStartCaching targetSize:targetSize contentMode:PHImageContentModeAspectFill options:nil]; [self.imageManager stopCachingImagesForAssets:assetsToStopCaching targetSize:targetSize contentMode:PHImageContentModeAspectFill options:nil]; self.previousPreheatRect = preheatRect; } } - (void)computeDifferenceBetweenRect:(CGRect)oldRect andRect:(CGRect)newRect addedHandler:(void (^)(CGRect addedRect))addedHandler removedHandler:(void (^)(CGRect removedRect))removedHandler { if (CGRectIntersectsRect(newRect, oldRect)) { CGFloat oldMaxY = CGRectGetMaxY(oldRect); CGFloat oldMinY = CGRectGetMinY(oldRect); CGFloat newMaxY = CGRectGetMaxY(newRect); CGFloat newMinY = CGRectGetMinY(newRect); if (newMaxY > oldMaxY) { CGRect rectToAdd = CGRectMake(newRect.origin.x, oldMaxY, newRect.size.width, (newMaxY - oldMaxY)); addedHandler(rectToAdd); } if (oldMinY > newMinY) { CGRect rectToAdd = CGRectMake(newRect.origin.x, newMinY, newRect.size.width, (oldMinY - newMinY)); addedHandler(rectToAdd); } if (newMaxY < oldMaxY) { CGRect rectToRemove = CGRectMake(newRect.origin.x, newMaxY, newRect.size.width, (oldMaxY - newMaxY)); removedHandler(rectToRemove); } if (oldMinY < newMinY) { CGRect rectToRemove = CGRectMake(newRect.origin.x, oldMinY, newRect.size.width, (newMinY - oldMinY)); removedHandler(rectToRemove); } } else { addedHandler(newRect); removedHandler(oldRect); } } - (NSArray *)assetsAtIndexPaths:(NSArray *)indexPaths { if (indexPaths.count == 0) { return nil; } NSMutableArray *assets = [NSMutableArray arrayWithCapacity:indexPaths.count]; for (NSIndexPath *indexPath in indexPaths) { if (indexPath.item < self.fetchResult.count) { PHAsset *asset = self.fetchResult[indexPath.item]; [assets addObject:asset]; } } return assets; } #pragma mark - PHPhotoLibraryChangeObserver - (void)photoLibraryDidChange:(PHChange *)changeInstance { dispatch_async(dispatch_get_main_queue(), ^{ PHFetchResultChangeDetails *collectionChanges = [changeInstance changeDetailsForFetchResult:self.fetchResult]; if (collectionChanges) { // Get the new fetch result self.fetchResult = [collectionChanges fetchResultAfterChanges]; if ([collectionChanges hasMoves] || [collectionChanges changedIndexes].count || ![collectionChanges hasIncrementalChanges]) { // We need to reload all if the incremental diffs are not available [self.collectionView reloadData]; } else { // If we have incremental diffs, tell the collection view to animate insertions and deletions [self.collectionView performBatchUpdates:^{ NSIndexSet *removedIndexes = [collectionChanges removedIndexes]; if ([removedIndexes count]) { [self.collectionView deleteItemsAtIndexPaths:[removedIndexes qb_indexPathsFromIndexesWithSection:0]]; } NSIndexSet *insertedIndexes = [collectionChanges insertedIndexes]; if ([insertedIndexes count]) { [self.collectionView insertItemsAtIndexPaths:[insertedIndexes qb_indexPathsFromIndexesWithSection:0]]; } } completion:NULL]; } [self resetCachedAssets]; } }); } #pragma mark - UIScrollViewDelegate - (void)scrollViewDidScroll:(UIScrollView *)scrollView { [self updateCachedAssets]; } #pragma mark - UICollectionViewDataSource - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView { return 1; } - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { return self.fetchResult.count; } - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { UGCKitAssetCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"AssetCell" forIndexPath:indexPath]; cell.tag = indexPath.item; cell.selectionColor = self.theme.pickerSelectionBorderColor; cell.showsOverlayViewWhenSelected = self.imagePickerController.allowsMultipleSelection; // Image PHAsset *asset = self.fetchResult[indexPath.item]; CGSize itemSize = [(UICollectionViewFlowLayout *)collectionView.collectionViewLayout itemSize]; CGSize targetSize = CGSizeScale(itemSize, self.traitCollection.displayScale); [self.imageManager requestImageForAsset:asset targetSize:targetSize contentMode:PHImageContentModeAspectFill options:nil resultHandler:^(UIImage *result, NSDictionary *info) { if (cell.tag == indexPath.item) { cell.imageView.image = result; } }]; // Video indicator if (asset.mediaType == PHAssetMediaTypeVideo) { cell.videoIndicatorView.hidden = NO; NSInteger minutes = (NSInteger)(asset.duration / 60.0); NSInteger seconds = (NSInteger)ceil(asset.duration - 60.0 * (double)minutes); cell.videoIndicatorView.timeLabel.text = [NSString stringWithFormat:@"%02ld:%02ld", (long)minutes, (long)seconds]; if (asset.mediaSubtypes & PHAssetMediaSubtypeVideoHighFrameRate) { cell.videoIndicatorView.videoIcon.hidden = YES; cell.videoIndicatorView.slomoIcon.hidden = NO; } else { cell.videoIndicatorView.videoIcon.hidden = NO; cell.videoIndicatorView.slomoIcon.hidden = YES; } } else { cell.videoIndicatorView.hidden = YES; } // Selection state if ([self.imagePickerController.selectedAssets containsObject:asset]) { [cell setSelected:YES]; [collectionView selectItemAtIndexPath:indexPath animated:NO scrollPosition:UICollectionViewScrollPositionNone]; } return cell; } - (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath { if (kind == UICollectionElementKindSectionFooter) { UICollectionReusableView *footerView = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionFooter withReuseIdentifier:@"FooterView" forIndexPath:indexPath]; // Number of assets UILabel *label = (UILabel *)[footerView viewWithTag:1]; label.textColor = _theme.titleColor; NSUInteger numberOfPhotos = [self.fetchResult countOfAssetsWithMediaType:PHAssetMediaTypeImage]; NSUInteger numberOfVideos = [self.fetchResult countOfAssetsWithMediaType:PHAssetMediaTypeVideo]; switch (self.imagePickerController.mediaType) { case UGCKitMediaTypeAny: { NSString *format; if (numberOfPhotos == 1) { if (numberOfVideos == 1) { format = [_theme localizedString:@"UGCKit.MediaPicker.Footer.PhotoAndVideo"]; } else { format = [_theme localizedString:@"UGCKit.MediaPicker.Footer.PhotoAndVideos"]; } } else if (numberOfVideos == 1) { format = [_theme localizedString:@"UGCKit.MediaPicker.Footer.PhotosAndVideo"]; } else { format = [_theme localizedString:@"UGCKit.MediaPicker.Footer.PhotosAndVideos"]; } label.text = [NSString stringWithFormat:format, numberOfPhotos, numberOfVideos]; } break; case UGCKitMediaTypePhoto: { NSString *key = (numberOfPhotos == 1) ? @"UGCKit.MediaPicker.Footer.Photo" : @"UGCKit.MediaPicker.Footer.Photos"; NSString *format = [_theme localizedString:key]; label.text = [NSString stringWithFormat:format, numberOfPhotos]; } break; case UGCKitMediaTypeVideo: { NSString *key = (numberOfVideos == 1) ? @"UGCKit.MediaPicker.Footer.Video" : @"UGCKit.MediaPicker.Footer.Videos"; NSString *format = [_theme localizedString:key]; label.text = [NSString stringWithFormat:format, numberOfVideos]; } break; } return footerView; } return [[UICollectionReusableView alloc] init]; } #pragma mark - UICollectionViewDelegate - (BOOL)collectionView:(UICollectionView *)collectionView shouldSelectItemAtIndexPath:(NSIndexPath *)indexPath { if ([self.imagePickerController.delegate respondsToSelector:@selector(qb_imagePickerController:shouldSelectAsset:)]) { PHAsset *asset = self.fetchResult[indexPath.item]; return [self.imagePickerController.delegate qb_imagePickerController:self.imagePickerController shouldSelectAsset:asset]; } if ([self isAutoDeselectEnabled]) { return YES; } return ![self isMaximumSelectionLimitReached]; } - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath { UGCKitMediaPickerViewController *imagePickerController = self.imagePickerController; NSMutableOrderedSet *selectedAssets = imagePickerController.selectedAssets; PHAsset *asset = self.fetchResult[indexPath.item]; if (imagePickerController.allowsMultipleSelection) { if ([self isAutoDeselectEnabled] && selectedAssets.count > 0) { // Remove previous selected asset from set [selectedAssets removeObjectAtIndex:0]; // Deselect previous selected asset if (self.lastSelectedItemIndexPath) { [collectionView deselectItemAtIndexPath:self.lastSelectedItemIndexPath animated:NO]; } } // Add asset to set [selectedAssets addObject:asset]; [self.imagesController addAsset:asset]; self.lastSelectedItemIndexPath = indexPath; [self updateDoneButtonState]; if (imagePickerController.showsNumberOfSelectedAssets) { [self updateSelectionInfo]; } } else { [self tellDelegateWithAssets:@[asset]]; // if ([imagePickerController.delegate respondsToSelector:@selector(qb_imagePickerController:didFinishPickingAssets:)]) { // [imagePickerController.delegate qb_imagePickerController:imagePickerController didFinishPickingAssets:@[asset]]; // } } if ([imagePickerController.delegate respondsToSelector:@selector(qb_imagePickerController:didSelectAsset:)]) { [imagePickerController.delegate qb_imagePickerController:imagePickerController didSelectAsset:asset]; } } - (void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath { if (!self.imagePickerController.allowsMultipleSelection) { return; } UGCKitMediaPickerViewController *imagePickerController = self.imagePickerController; NSMutableOrderedSet *selectedAssets = imagePickerController.selectedAssets; PHAsset *asset = self.fetchResult[indexPath.item]; // Remove asset from set NSUInteger index = [selectedAssets indexOfObject:asset]; [selectedAssets removeObject:asset]; if (index != NSNotFound) { [self.imagesController removeAssetAtIndex:index]; } self.lastSelectedItemIndexPath = nil; [self updateDoneButtonState]; if (imagePickerController.showsNumberOfSelectedAssets) { [self updateSelectionInfo]; } if ([imagePickerController.delegate respondsToSelector:@selector(qb_imagePickerController:didDeselectAsset:)]) { [imagePickerController.delegate qb_imagePickerController:imagePickerController didDeselectAsset:asset]; } } #pragma mark - UICollectionViewDelegateFlowLayout - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath { NSUInteger numberOfColumns; if (UIInterfaceOrientationIsPortrait([[UIApplication sharedApplication] statusBarOrientation])) { numberOfColumns = self.imagePickerController.config.numberOfColumnsInPortrait; } else { numberOfColumns = self.imagePickerController.config.numberOfColumnsInLandscape; } CGFloat width = (CGRectGetWidth(self.view.frame) - 2.0 * (numberOfColumns - 1)) / numberOfColumns; return CGSizeMake(width, width); } @end