/* MIT License (MIT) Copyright (c) 2015 Clement CN Tsang 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 "CTAssetsPickerDefines.h" #import "CTAssetsPickerController.h" #import "CTAssetsPickerController+Internal.h" #import "CTAssetsGridViewController.h" #import "CTAssetsGridView.h" #import "CTAssetsGridViewLayout.h" #import "CTAssetsGridViewCell.h" #import "CTAssetsGridViewFooter.h" #import "CTAssetsPickerNoAssetsView.h" #import "CTAssetsPageViewController.h" #import "CTAssetsPageViewController+Internal.h" #import "CTAssetsViewControllerTransition.h" #import "UICollectionView+CTAssetsPickerController.h" #import "NSIndexSet+CTAssetsPickerController.h" #import "NSBundle+CTAssetsPickerController.h" #import "PHImageManager+CTAssetsPickerController.h" NSString * const CTAssetsGridViewCellIdentifier = @"CTAssetsGridViewCellIdentifier"; NSString * const CTAssetsGridViewFooterIdentifier = @"CTAssetsGridViewFooterIdentifier"; @interface CTAssetsGridViewController () @property (nonatomic, weak) CTAssetsPickerController *picker; @property (nonatomic, strong) PHFetchResult *fetchResult; @property (nonatomic, strong) PHCachingImageManager *imageManager; @property (nonatomic, assign) CGRect previousPreheatRect; @property (nonatomic, assign) CGRect previousBounds; @property (nonatomic, strong) CTAssetsGridViewFooter *footer; @property (nonatomic, strong) CTAssetsPickerNoAssetsView *noAssetsView; @property (nonatomic, assign) BOOL didLayoutSubviews; @end @implementation CTAssetsGridViewController - (instancetype)init { CTAssetsGridViewLayout *layout = [CTAssetsGridViewLayout new]; if (self = [super initWithCollectionViewLayout:layout]) { _imageManager = [PHCachingImageManager new]; self.extendedLayoutIncludesOpaqueBars = YES; self.collectionView.allowsMultipleSelection = YES; [self.collectionView registerClass:CTAssetsGridViewCell.class forCellWithReuseIdentifier:CTAssetsGridViewCellIdentifier]; [self.collectionView registerClass:CTAssetsGridViewFooter.class forSupplementaryViewOfKind:UICollectionElementKindSectionFooter withReuseIdentifier:CTAssetsGridViewFooterIdentifier]; [self addNotificationObserver]; } return self; } - (void)viewDidLoad { [super viewDidLoad]; [self setupViews]; [self registerChangeObserver]; [self addGestureRecognizer]; [self addNotificationObserver]; [self resetCachedAssetImages]; } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; [self setupButtons]; [self setupAssets]; [self updateTitle:self.picker.selectedAssets]; [self updateButton:self.picker.selectedAssets]; } - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; [self updateCachedAssetImages]; } - (void)viewWillLayoutSubviews { [super viewWillLayoutSubviews]; if (!CGRectEqualToRect(self.view.bounds, self.previousBounds)) { [self updateCollectionViewLayout]; self.previousBounds = self.view.bounds; } } - (void)viewDidLayoutSubviews { [super viewDidLayoutSubviews]; if (!self.didLayoutSubviews && self.fetchResult.count > 0) { [self scrollToBottomIfNeeded]; self.didLayoutSubviews = YES; } } - (void)dealloc { [self unregisterChangeObserver]; [self removeNotificationObserver]; } #pragma mark - Accessors - (CTAssetsPickerController *)picker { return (CTAssetsPickerController *)self.splitViewController.parentViewController; } - (PHAsset *)assetAtIndexPath:(NSIndexPath *)indexPath { return (self.fetchResult.count > 0) ? self.fetchResult[indexPath.item] : nil; } #pragma mark - Setup - (void)setupViews { self.collectionView.backgroundColor = [UIColor colorWithWhite:0 alpha:0]; CTAssetsGridView *gridView = [CTAssetsGridView new]; [self.view insertSubview:gridView atIndex:0]; [self.view setNeedsUpdateConstraints]; } - (void)setupButtons { if (self.navigationItem.rightBarButtonItem == nil) { NSString *title = (self.picker.doneButtonTitle) ? self.picker.doneButtonTitle : CTAssetsPickerLocalizedString(@"Done", nil); self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:title style:UIBarButtonItemStyleDone target:self.picker action:@selector(finishPickingAssets:)]; } } - (void)setupAssets { PHFetchResult *fetchResult = [PHAsset fetchAssetsInAssetCollection:self.assetCollection options:self.picker.assetsFetchOptions]; self.fetchResult = fetchResult; [self reloadData]; } #pragma mark - Collection view layout - (void)updateCollectionViewLayout { UITraitCollection *trait = self.traitCollection; CGSize contentSize = self.view.bounds.size; UICollectionViewLayout *layout; NSArray *attributes = [self.collectionView.collectionViewLayout layoutAttributesForElementsInRect:self.collectionView.bounds]; UICollectionViewLayoutAttributes *attr = (UICollectionViewLayoutAttributes*)attributes.firstObject; // new content size should be at least of first item size, else ignoring if (contentSize.width < attr.size.width || contentSize.height < attr.size.height) { return; } if ([self.picker.delegate respondsToSelector:@selector(assetsPickerController:collectionViewLayoutForContentSize:traitCollection:)]) { layout = [self.picker.delegate assetsPickerController:self.picker collectionViewLayoutForContentSize:contentSize traitCollection:trait]; } else { layout = [[CTAssetsGridViewLayout alloc] initWithContentSize:contentSize traitCollection:trait]; } __weak CTAssetsGridViewController *weakSelf = self; [self.collectionView setCollectionViewLayout:layout animated:NO completion:^(BOOL finished){ [weakSelf.collectionView reloadItemsAtIndexPaths:[weakSelf.collectionView indexPathsForVisibleItems]]; }]; } #pragma mark - Scroll to bottom - (void)scrollToBottomIfNeeded { BOOL shouldScrollToBottom; if ([self.picker.delegate respondsToSelector:@selector(assetsPickerController:shouldScrollToBottomForAssetCollection:)]) shouldScrollToBottom = [self.picker.delegate assetsPickerController:self.picker shouldScrollToBottomForAssetCollection:self.assetCollection]; else shouldScrollToBottom = YES; if (shouldScrollToBottom) { NSIndexPath *indexPath = [NSIndexPath indexPathForItem:self.fetchResult.count-1 inSection:0]; [self.collectionView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionTop animated:NO]; } } #pragma mark - Notifications - (void)addNotificationObserver { NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; [center addObserver:self selector:@selector(assetsPickerSelectedAssetsDidChange:) name:CTAssetsPickerSelectedAssetsDidChangeNotification object:nil]; [center addObserver:self selector:@selector(assetsPickerDidSelectAsset:) name:CTAssetsPickerDidSelectAssetNotification object:nil]; [center addObserver:self selector:@selector(assetsPickerDidDeselectAsset:) name:CTAssetsPickerDidDeselectAssetNotification object:nil]; } - (void)removeNotificationObserver { NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; [center removeObserver:self name:CTAssetsPickerSelectedAssetsDidChangeNotification object:nil]; [center removeObserver:self name:CTAssetsPickerDidSelectAssetNotification object:nil]; [center removeObserver:self name:CTAssetsPickerDidDeselectAssetNotification object:nil]; } #pragma mark - Photo library change observer - (void)registerChangeObserver { [[PHPhotoLibrary sharedPhotoLibrary] registerChangeObserver:self]; } - (void)unregisterChangeObserver { [[PHPhotoLibrary sharedPhotoLibrary] unregisterChangeObserver:self]; } #pragma mark - Photo library changed - (void)photoLibraryDidChange:(PHChange *)changeInstance { // Call might come on any background queue. Re-dispatch to the main queue to handle it. dispatch_async(dispatch_get_main_queue(), ^{ PHFetchResultChangeDetails *changeDetails = [changeInstance changeDetailsForFetchResult:self.fetchResult]; if (changeDetails) { self.fetchResult = changeDetails.fetchResultAfterChanges; UICollectionView *collectionView = self.collectionView; if (!changeDetails.hasIncrementalChanges || changeDetails.hasMoves) { [collectionView reloadData]; [self resetCachedAssetImages]; } else { NSArray *removedPaths; NSArray *insertedPaths; NSArray *changedPaths; NSIndexSet *removedIndexes = changeDetails.removedIndexes; removedPaths = [removedIndexes ctassetsPickerIndexPathsFromIndexesWithSection:0]; NSIndexSet *insertedIndexes = changeDetails.insertedIndexes; insertedPaths = [insertedIndexes ctassetsPickerIndexPathsFromIndexesWithSection:0]; NSIndexSet *changedIndexes = changeDetails.changedIndexes; changedPaths = [changedIndexes ctassetsPickerIndexPathsFromIndexesWithSection:0]; BOOL shouldReload = NO; if (changedPaths != nil && removedPaths != nil) { for (NSIndexPath *changedPath in changedPaths) { if ([removedPaths containsObject:changedPath]) { shouldReload = YES; break; } } } if (removedPaths.lastObject && ((NSIndexPath *)removedPaths.lastObject).item >= self.fetchResult.count) { shouldReload = YES; } if (shouldReload) { [collectionView reloadData]; } else { // if we have incremental diffs, tell the collection view to animate insertions and deletions [collectionView performBatchUpdates:^{ if (removedPaths.count) { [collectionView deleteItemsAtIndexPaths:[removedIndexes ctassetsPickerIndexPathsFromIndexesWithSection:0]]; } if (insertedPaths.count) { [collectionView insertItemsAtIndexPaths:[insertedIndexes ctassetsPickerIndexPathsFromIndexesWithSection:0]]; } if (changedPaths.count) { [collectionView reloadItemsAtIndexPaths:[changedIndexes ctassetsPickerIndexPathsFromIndexesWithSection:0] ]; } } completion:^(BOOL finished){ if (finished) [self resetCachedAssetImages]; }]; } } [self.footer bind:self.fetchResult]; if (self.fetchResult.count == 0) [self showNoAssets]; else [self hideNoAssets]; } if ([self.delegate respondsToSelector:@selector(assetsGridViewController:photoLibraryDidChangeForAssetCollection:)]) [self.delegate assetsGridViewController:self photoLibraryDidChangeForAssetCollection:self.assetCollection]; }); } #pragma mark - Selected assets changed - (void)assetsPickerSelectedAssetsDidChange:(NSNotification *)notification { NSArray *selectedAssets = (NSArray *)notification.object; [self updateTitle:selectedAssets]; [self updateButton:selectedAssets]; } - (void)updateTitle:(NSArray *)selectedAssets { if (selectedAssets.count > 0) self.title = self.picker.selectedAssetsString; else self.title = self.assetCollection.localizedTitle; } - (void)updateButton:(NSArray *)selectedAssets { if (self.picker.alwaysEnableDoneButton) self.navigationItem.rightBarButtonItem.enabled = YES; else self.navigationItem.rightBarButtonItem.enabled = (self.picker.selectedAssets.count > 0); } #pragma mark - Did de/select asset notifications - (void)assetsPickerDidSelectAsset:(NSNotification *)notification { PHAsset *asset = (PHAsset *)notification.object; NSIndexPath *indexPath = [NSIndexPath indexPathForItem:[self.fetchResult indexOfObject:asset] inSection:0]; [self.collectionView selectItemAtIndexPath:indexPath animated:NO scrollPosition:UICollectionViewScrollPositionNone]; [self updateSelectionOrderLabels]; } - (void)assetsPickerDidDeselectAsset:(NSNotification *)notification { PHAsset *asset = (PHAsset *)notification.object; NSIndexPath *indexPath = [NSIndexPath indexPathForItem:[self.fetchResult indexOfObject:asset] inSection:0]; [self.collectionView deselectItemAtIndexPath:indexPath animated:NO]; [self updateSelectionOrderLabels]; } #pragma mark - Update Selection Order Labels - (void)updateSelectionOrderLabels { for (NSIndexPath *indexPath in [self.collectionView indexPathsForSelectedItems]) { PHAsset *asset = [self assetAtIndexPath:indexPath]; CTAssetsGridViewCell *cell = (CTAssetsGridViewCell *)[self.collectionView cellForItemAtIndexPath:indexPath]; cell.selectionIndex = [self.picker.selectedAssets indexOfObject:asset]; } } #pragma mark - Gesture recognizer - (void)addGestureRecognizer { UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(pushPageViewController:)]; [self.collectionView addGestureRecognizer:longPress]; } #pragma mark - Push assets page view controller - (void)pushPageViewController:(UILongPressGestureRecognizer *)longPress { if (longPress.state == UIGestureRecognizerStateBegan) { CGPoint point = [longPress locationInView:self.collectionView]; NSIndexPath *indexPath = [self.collectionView indexPathForItemAtPoint:point]; CTAssetsPageViewController *vc = [[CTAssetsPageViewController alloc] initWithFetchResult:self.fetchResult]; vc.allowsSelection = YES; vc.pageIndex = indexPath.item; [self.navigationController pushViewController:vc animated:YES]; } } #pragma mark - Reload data - (void)reloadData { if (self.fetchResult.count > 0) { [self hideNoAssets]; [self.collectionView reloadData]; } else { [self showNoAssets]; } } #pragma mark - Asset images caching - (void)resetCachedAssetImages { [self.imageManager stopCachingImagesForAllAssets]; self.previousPreheatRect = CGRectZero; } - (void)updateCachedAssetImages { 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.0f, -0.5f * CGRectGetHeight(preheatRect)); // If scrolled by a "reasonable" amount... CGFloat delta = ABS(CGRectGetMidY(preheatRect) - CGRectGetMidY(self.previousPreheatRect)); if (delta > CGRectGetHeight(self.collectionView.bounds) / 3.0f) { // Compute the assets to start caching and to stop caching. NSMutableArray *addedIndexPaths = [NSMutableArray array]; NSMutableArray *removedIndexPaths = [NSMutableArray array]; [self computeDifferenceBetweenRect:self.previousPreheatRect andRect:preheatRect removedHandler:^(CGRect removedRect) { NSArray *indexPaths = [self.collectionView ctassetsPickerIndexPathsForElementsInRect:removedRect]; [removedIndexPaths addObjectsFromArray:indexPaths]; } addedHandler:^(CGRect addedRect) { NSArray *indexPaths = [self.collectionView ctassetsPickerIndexPathsForElementsInRect:addedRect]; [addedIndexPaths addObjectsFromArray:indexPaths]; }]; [self startCachingThumbnailsForIndexPaths:addedIndexPaths]; [self stopCachingThumbnailsForIndexPaths:removedIndexPaths]; self.previousPreheatRect = preheatRect; } } - (void)startCachingThumbnailsForIndexPaths:(NSArray *)indexPaths { for (NSIndexPath *indexPath in indexPaths) { PHAsset *asset = [self assetAtIndexPath:indexPath]; if (!asset) break; UICollectionViewLayoutAttributes *attributes = [self.collectionView.collectionViewLayout layoutAttributesForItemAtIndexPath:indexPath]; CGSize targetSize = [self.picker imageSizeForContainerSize:attributes.size]; [self.imageManager startCachingImagesForAssets:@[asset] targetSize:targetSize contentMode:PHImageContentModeAspectFill options:self.picker.thumbnailRequestOptions]; } } - (void)stopCachingThumbnailsForIndexPaths:(NSArray *)indexPaths { for (NSIndexPath *indexPath in indexPaths) { PHAsset *asset = [self assetAtIndexPath:indexPath]; if (!asset) break; UICollectionViewLayoutAttributes *attributes = [self.collectionView.collectionViewLayout layoutAttributesForItemAtIndexPath:indexPath]; CGSize targetSize = [self.picker imageSizeForContainerSize:attributes.size]; [self.imageManager stopCachingImagesForAssets:@[asset] targetSize:targetSize contentMode:PHImageContentModeAspectFill options:self.picker.thumbnailRequestOptions]; } } - (void)computeDifferenceBetweenRect:(CGRect)oldRect andRect:(CGRect)newRect removedHandler:(void (^)(CGRect removedRect))removedHandler addedHandler:(void (^)(CGRect addedRect))addedHandler { 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); } } #pragma mark - Scroll view delegate - (void)scrollViewDidScroll:(UIScrollView *)scrollView { [self updateCachedAssetImages]; } #pragma mark - No assets - (void)showNoAssets { CTAssetsPickerNoAssetsView *view = [CTAssetsPickerNoAssetsView new]; [self.view addSubview:view]; [view setNeedsUpdateConstraints]; [view updateConstraintsIfNeeded]; self.noAssetsView = view; } - (void)hideNoAssets { if (self.noAssetsView) { [self.noAssetsView removeFromSuperview]; self.noAssetsView = nil; } } #pragma mark - Collection view data source - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView { return 1; } - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { return self.fetchResult.count; } - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { CTAssetsGridViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:CTAssetsGridViewCellIdentifier forIndexPath:indexPath]; PHAsset *asset = [self assetAtIndexPath:indexPath]; if ([self.picker.delegate respondsToSelector:@selector(assetsPickerController:shouldEnableAsset:)]) cell.enabled = [self.picker.delegate assetsPickerController:self.picker shouldEnableAsset:asset]; else cell.enabled = YES; cell.showsSelectionIndex = self.picker.showsSelectionIndex; // XXX // Setting `selected` property blocks further deselection. // Have to call selectItemAtIndexPath too. ( ref: http://stackoverflow.com/a/17812116/1648333 ) if ([self.picker.selectedAssets containsObject:asset]) { cell.selected = YES; cell.selectionIndex = [self.picker.selectedAssets indexOfObject:asset]; [collectionView selectItemAtIndexPath:indexPath animated:NO scrollPosition:UICollectionViewScrollPositionNone]; } [cell bind:asset]; UICollectionViewLayoutAttributes *attributes = [collectionView.collectionViewLayout layoutAttributesForItemAtIndexPath:indexPath]; CGSize targetSize = [self.picker imageSizeForContainerSize:attributes.size]; [self requestThumbnailForCell:cell targetSize:targetSize asset:asset]; return cell; } - (void)requestThumbnailForCell:(CTAssetsGridViewCell *)cell targetSize:(CGSize)targetSize asset:(PHAsset *)asset { NSInteger tag = cell.tag + 1; cell.tag = tag; [self.imageManager ctassetsPickerRequestImageForAsset:asset targetSize:targetSize contentMode:PHImageContentModeAspectFill options:self.picker.thumbnailRequestOptions resultHandler:^(UIImage *image, NSDictionary *info){ // Only update the image if the cell tag hasn't changed. Otherwise, the cell has been re-used. if (cell.tag == tag) [(CTAssetThumbnailView *)cell.backgroundView bind:image asset:asset]; }]; } - (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath { CTAssetsGridViewFooter *footer = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionFooter withReuseIdentifier:CTAssetsGridViewFooterIdentifier forIndexPath:indexPath]; [footer bind:self.fetchResult]; self.footer = footer; return footer; } #pragma mark - Collection view delegate - (BOOL)collectionView:(UICollectionView *)collectionView shouldSelectItemAtIndexPath:(NSIndexPath *)indexPath { PHAsset *asset = [self assetAtIndexPath:indexPath]; CTAssetsGridViewCell *cell = (CTAssetsGridViewCell *)[collectionView cellForItemAtIndexPath:indexPath]; if (!cell.isEnabled) return NO; else if ([self.picker.delegate respondsToSelector:@selector(assetsPickerController:shouldSelectAsset:)]) return [self.picker.delegate assetsPickerController:self.picker shouldSelectAsset:asset]; else return YES; } - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath { PHAsset *asset = [self assetAtIndexPath:indexPath]; [self.picker selectAsset:asset]; if ([self.picker.delegate respondsToSelector:@selector(assetsPickerController:didSelectAsset:)]) [self.picker.delegate assetsPickerController:self.picker didSelectAsset:asset]; } - (BOOL)collectionView:(UICollectionView *)collectionView shouldDeselectItemAtIndexPath:(NSIndexPath *)indexPath { PHAsset *asset = [self assetAtIndexPath:indexPath]; if ([self.picker.delegate respondsToSelector:@selector(assetsPickerController:shouldDeselectAsset:)]) return [self.picker.delegate assetsPickerController:self.picker shouldDeselectAsset:asset]; else return YES; } - (void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath { PHAsset *asset = [self assetAtIndexPath:indexPath]; [self.picker deselectAsset:asset]; if ([self.picker.delegate respondsToSelector:@selector(assetsPickerController:didDeselectAsset:)]) [self.picker.delegate assetsPickerController:self.picker didDeselectAsset:asset]; } - (BOOL)collectionView:(UICollectionView *)collectionView shouldHighlightItemAtIndexPath:(NSIndexPath *)indexPath { PHAsset *asset = [self assetAtIndexPath:indexPath]; if ([self.picker.delegate respondsToSelector:@selector(assetsPickerController:shouldHighlightAsset:)]) return [self.picker.delegate assetsPickerController:self.picker shouldHighlightAsset:asset]; else return YES; } - (void)collectionView:(UICollectionView *)collectionView didHighlightItemAtIndexPath:(NSIndexPath *)indexPath { PHAsset *asset = [self assetAtIndexPath:indexPath]; if ([self.picker.delegate respondsToSelector:@selector(assetsPickerController:didHighlightAsset:)]) [self.picker.delegate assetsPickerController:self.picker didHighlightAsset:asset]; } - (void)collectionView:(UICollectionView *)collectionView didUnhighlightItemAtIndexPath:(NSIndexPath *)indexPath { PHAsset *asset = [self assetAtIndexPath:indexPath]; if ([self.picker.delegate respondsToSelector:@selector(assetsPickerController:didUnhighlightAsset:)]) [self.picker.delegate assetsPickerController:self.picker didUnhighlightAsset:asset]; } @end