612 lines
23 KiB
Mathematica
612 lines
23 KiB
Mathematica
|
//
|
||
|
// GMGridViewController.m
|
||
|
// GMPhotoPicker
|
||
|
//
|
||
|
// Created by Guillermo Muntaner Perelló on 19/09/14.
|
||
|
// Copyright (c) 2014 Guillermo Muntaner Perelló. All rights reserved.
|
||
|
//
|
||
|
|
||
|
#import "GMGridViewController.h"
|
||
|
#import "GMImagePickerController.h"
|
||
|
#import "GMAlbumsViewController.h"
|
||
|
#import "GMGridViewCell.h"
|
||
|
|
||
|
#include <Photos/Photos.h>
|
||
|
|
||
|
|
||
|
//Helper methods
|
||
|
@implementation NSIndexSet (Convenience)
|
||
|
- (NSArray *)aapl_indexPathsFromIndexesWithSection:(NSUInteger)section {
|
||
|
NSMutableArray *indexPaths = [NSMutableArray arrayWithCapacity:self.count];
|
||
|
[self enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
|
||
|
[indexPaths addObject:[NSIndexPath indexPathForItem:(NSInteger)idx inSection:(NSInteger)section]];
|
||
|
}];
|
||
|
return indexPaths;
|
||
|
}
|
||
|
@end
|
||
|
|
||
|
@implementation UICollectionView (Convenience)
|
||
|
- (NSArray *)aapl_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 GMImagePickerController ()
|
||
|
|
||
|
- (void)finishPickingAssets:(id)sender;
|
||
|
- (void)dismiss:(id)sender;
|
||
|
- (NSString *)toolbarTitle;
|
||
|
- (UIView *)noAssetsView;
|
||
|
|
||
|
@end
|
||
|
|
||
|
|
||
|
@interface GMGridViewController () <PHPhotoLibraryChangeObserver>
|
||
|
|
||
|
@property (nonatomic, weak) GMImagePickerController *picker;
|
||
|
@property (strong,nonatomic) PHCachingImageManager *imageManager;
|
||
|
@property (assign, nonatomic) CGRect previousPreheatRect;
|
||
|
|
||
|
@end
|
||
|
|
||
|
static CGSize AssetGridThumbnailSize;
|
||
|
NSString * const GMGridViewCellIdentifier = @"GMGridViewCellIdentifier";
|
||
|
|
||
|
@implementation GMGridViewController
|
||
|
{
|
||
|
CGFloat screenWidth;
|
||
|
CGFloat screenHeight;
|
||
|
UICollectionViewFlowLayout *portraitLayout;
|
||
|
UICollectionViewFlowLayout *landscapeLayout;
|
||
|
}
|
||
|
|
||
|
-(id)initWithPicker:(GMImagePickerController *)picker
|
||
|
{
|
||
|
//Custom init. The picker contains custom information to create the FlowLayout
|
||
|
self.picker = picker;
|
||
|
|
||
|
//Ipad popover is not affected by rotation!
|
||
|
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
|
||
|
{
|
||
|
screenWidth = CGRectGetWidth(picker.view.bounds);
|
||
|
screenHeight = CGRectGetHeight(picker.view.bounds);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if(UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation))
|
||
|
{
|
||
|
screenHeight = CGRectGetWidth(picker.view.bounds);
|
||
|
screenWidth = CGRectGetHeight(picker.view.bounds);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
screenWidth = CGRectGetWidth(picker.view.bounds);
|
||
|
screenHeight = CGRectGetHeight(picker.view.bounds);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
UICollectionViewFlowLayout *layout = [self collectionViewFlowLayoutForOrientation:[UIApplication sharedApplication].statusBarOrientation];
|
||
|
if (self = [super initWithCollectionViewLayout:layout])
|
||
|
{
|
||
|
//Compute the thumbnail pixel size:
|
||
|
CGFloat scale = [UIScreen mainScreen].scale;
|
||
|
//NSLog(@"This is @%fx scale device", scale);
|
||
|
if(scale >= 3)
|
||
|
{
|
||
|
scale = 2;
|
||
|
}
|
||
|
|
||
|
AssetGridThumbnailSize = CGSizeMake(layout.itemSize.width * scale, layout.itemSize.height * scale);
|
||
|
|
||
|
self.collectionView.allowsMultipleSelection = picker.allowsMultipleSelection;
|
||
|
|
||
|
[self.collectionView registerClass:GMGridViewCell.class
|
||
|
forCellWithReuseIdentifier:GMGridViewCellIdentifier];
|
||
|
|
||
|
self.preferredContentSize = kPopoverContentSize;
|
||
|
}
|
||
|
|
||
|
return self;
|
||
|
}
|
||
|
|
||
|
|
||
|
- (void)viewDidLoad
|
||
|
{
|
||
|
[super viewDidLoad];
|
||
|
[self setupViews];
|
||
|
|
||
|
// Navigation bar customization
|
||
|
if (self.picker.customNavigationBarPrompt) {
|
||
|
self.navigationItem.prompt = self.picker.customNavigationBarPrompt;
|
||
|
}
|
||
|
|
||
|
self.imageManager = [[PHCachingImageManager alloc] init];
|
||
|
[self resetCachedAssets];
|
||
|
[[PHPhotoLibrary sharedPhotoLibrary] registerChangeObserver:self];
|
||
|
|
||
|
if ([self respondsToSelector:@selector(setEdgesForExtendedLayout:)])
|
||
|
{
|
||
|
self.edgesForExtendedLayout = UIRectEdgeNone;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
- (void)viewWillAppear:(BOOL)animated
|
||
|
{
|
||
|
[super viewWillAppear:animated];
|
||
|
|
||
|
[self setupButtons];
|
||
|
[self setupToolbar];
|
||
|
}
|
||
|
|
||
|
- (void)viewDidAppear:(BOOL)animated
|
||
|
{
|
||
|
[super viewDidAppear:animated];
|
||
|
[self updateCachedAssets];
|
||
|
}
|
||
|
|
||
|
- (void)dealloc
|
||
|
{
|
||
|
[self resetCachedAssets];
|
||
|
[[PHPhotoLibrary sharedPhotoLibrary] unregisterChangeObserver:self];
|
||
|
}
|
||
|
|
||
|
- (UIStatusBarStyle)preferredStatusBarStyle {
|
||
|
return self.picker.pickerStatusBarStyle;
|
||
|
}
|
||
|
|
||
|
|
||
|
#pragma mark - Rotation
|
||
|
|
||
|
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
|
||
|
{
|
||
|
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
UICollectionViewFlowLayout *layout = [self collectionViewFlowLayoutForOrientation:toInterfaceOrientation];
|
||
|
|
||
|
//Update the AssetGridThumbnailSize:
|
||
|
CGFloat scale = [UIScreen mainScreen].scale;
|
||
|
AssetGridThumbnailSize = CGSizeMake(layout.itemSize.width * scale, layout.itemSize.height * scale);
|
||
|
|
||
|
[self resetCachedAssets];
|
||
|
//This is optional. Reload visible thumbnails:
|
||
|
for (GMGridViewCell *cell in [self.collectionView visibleCells]) {
|
||
|
NSInteger currentTag = cell.tag;
|
||
|
[self.imageManager requestImageForAsset:cell.asset
|
||
|
targetSize:AssetGridThumbnailSize
|
||
|
contentMode:PHImageContentModeAspectFill
|
||
|
options:nil
|
||
|
resultHandler:^(UIImage *result, NSDictionary *info)
|
||
|
{
|
||
|
// Only update the thumbnail if the cell tag hasn't changed. Otherwise, the cell has been re-used.
|
||
|
if (cell.tag == currentTag) {
|
||
|
[cell.imageView setImage:result];
|
||
|
}
|
||
|
}];
|
||
|
}
|
||
|
|
||
|
[self.collectionView setCollectionViewLayout:layout animated:YES];
|
||
|
}
|
||
|
|
||
|
|
||
|
#pragma mark - Setup
|
||
|
|
||
|
- (void)setupViews
|
||
|
{
|
||
|
self.collectionView.backgroundColor = [UIColor clearColor];
|
||
|
self.view.backgroundColor = [self.picker pickerBackgroundColor];
|
||
|
}
|
||
|
|
||
|
- (void)setupButtons
|
||
|
{
|
||
|
if (self.picker.allowsMultipleSelection) {
|
||
|
NSString *doneTitle = self.picker.customDoneButtonTitle ? self.picker.customDoneButtonTitle : NSLocalizedStringFromTableInBundle(@"picker.navigation.done-button", @"GMImagePicker", [NSBundle bundleForClass:GMImagePickerController.class], @"Done");
|
||
|
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:doneTitle
|
||
|
style:UIBarButtonItemStyleDone
|
||
|
target:self.picker
|
||
|
action:@selector(finishPickingAssets:)];
|
||
|
|
||
|
self.navigationItem.rightBarButtonItem.enabled = (self.picker.autoDisableDoneButton ? self.picker.selectedAssets.count > 0 : TRUE);
|
||
|
} else {
|
||
|
NSString *cancelTitle = self.picker.customCancelButtonTitle ? self.picker.customCancelButtonTitle : NSLocalizedStringFromTableInBundle(@"picker.navigation.cancel-button", @"GMImagePicker", [NSBundle bundleForClass:GMImagePickerController.class], @"Cancel");
|
||
|
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:cancelTitle
|
||
|
style:UIBarButtonItemStyleDone
|
||
|
target:self.picker
|
||
|
action:@selector(dismiss:)];
|
||
|
}
|
||
|
if (self.picker.useCustomFontForNavigationBar) {
|
||
|
if (self.picker.useCustomFontForNavigationBar) {
|
||
|
NSDictionary* barButtonItemAttributes = @{NSFontAttributeName: [UIFont fontWithName:self.picker.pickerFontName size:self.picker.pickerFontHeaderSize]};
|
||
|
[self.navigationItem.rightBarButtonItem setTitleTextAttributes:barButtonItemAttributes forState:UIControlStateNormal];
|
||
|
[self.navigationItem.rightBarButtonItem setTitleTextAttributes:barButtonItemAttributes forState:UIControlStateSelected];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
- (void)setupToolbar
|
||
|
{
|
||
|
self.toolbarItems = self.picker.toolbarItems;
|
||
|
}
|
||
|
|
||
|
|
||
|
#pragma mark - Collection View Layout
|
||
|
|
||
|
- (UICollectionViewFlowLayout *)collectionViewFlowLayoutForOrientation:(UIInterfaceOrientation)orientation
|
||
|
{
|
||
|
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
|
||
|
{
|
||
|
if(!portraitLayout)
|
||
|
{
|
||
|
portraitLayout = [[UICollectionViewFlowLayout alloc] init];
|
||
|
portraitLayout.minimumInteritemSpacing = self.picker.minimumInteritemSpacing;
|
||
|
int cellTotalUsableWidth = (int)(screenWidth - (self.picker.colsInPortrait-1)*self.picker.minimumInteritemSpacing);
|
||
|
portraitLayout.itemSize = CGSizeMake(cellTotalUsableWidth/self.picker.colsInPortrait, cellTotalUsableWidth/self.picker.colsInPortrait);
|
||
|
double cellTotalUsedWidth = (double)portraitLayout.itemSize.width*self.picker.colsInPortrait;
|
||
|
double spaceTotalWidth = (double)screenWidth-cellTotalUsedWidth;
|
||
|
double spaceWidth = spaceTotalWidth/(double)(self.picker.colsInPortrait-1);
|
||
|
portraitLayout.minimumLineSpacing = spaceWidth;
|
||
|
}
|
||
|
return portraitLayout;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if(UIInterfaceOrientationIsLandscape(orientation))
|
||
|
{
|
||
|
if(!landscapeLayout)
|
||
|
{
|
||
|
landscapeLayout = [[UICollectionViewFlowLayout alloc] init];
|
||
|
landscapeLayout.minimumInteritemSpacing = self.picker.minimumInteritemSpacing;
|
||
|
int cellTotalUsableWidth = (int)(screenHeight - (self.picker.colsInLandscape-1)*self.picker.minimumInteritemSpacing);
|
||
|
landscapeLayout.itemSize = CGSizeMake(cellTotalUsableWidth/self.picker.colsInLandscape, cellTotalUsableWidth/self.picker.colsInLandscape);
|
||
|
double cellTotalUsedWidth = (double)landscapeLayout.itemSize.width*self.picker.colsInLandscape;
|
||
|
double spaceTotalWidth = (double)screenHeight-cellTotalUsedWidth;
|
||
|
double spaceWidth = spaceTotalWidth/(double)(self.picker.colsInLandscape-1);
|
||
|
landscapeLayout.minimumLineSpacing = spaceWidth;
|
||
|
}
|
||
|
return landscapeLayout;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if(!portraitLayout)
|
||
|
{
|
||
|
portraitLayout = [[UICollectionViewFlowLayout alloc] init];
|
||
|
portraitLayout.minimumInteritemSpacing = self.picker.minimumInteritemSpacing;
|
||
|
int cellTotalUsableWidth = (int)(screenWidth - (self.picker.colsInPortrait-1) * self.picker.minimumInteritemSpacing);
|
||
|
portraitLayout.itemSize = CGSizeMake(cellTotalUsableWidth/self.picker.colsInPortrait, cellTotalUsableWidth/self.picker.colsInPortrait);
|
||
|
double cellTotalUsedWidth = (double)portraitLayout.itemSize.width*self.picker.colsInPortrait;
|
||
|
double spaceTotalWidth = (double)screenWidth-cellTotalUsedWidth;
|
||
|
double spaceWidth = spaceTotalWidth/(double)(self.picker.colsInPortrait-1);
|
||
|
portraitLayout.minimumLineSpacing = spaceWidth;
|
||
|
}
|
||
|
return portraitLayout;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
#pragma mark - Collection View Data Source
|
||
|
|
||
|
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
|
||
|
{
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
|
||
|
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
|
||
|
{
|
||
|
GMGridViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:GMGridViewCellIdentifier
|
||
|
forIndexPath:indexPath];
|
||
|
|
||
|
// Increment the cell's tag
|
||
|
NSInteger currentTag = cell.tag + 1;
|
||
|
cell.tag = currentTag;
|
||
|
|
||
|
PHAsset *asset = self.assetsFetchResults[(NSUInteger)indexPath.item];
|
||
|
|
||
|
/*if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
|
||
|
{
|
||
|
NSLog(@"Image manager: Requesting FIT image for iPad");
|
||
|
[self.imageManager requestImageForAsset:asset
|
||
|
targetSize:AssetGridThumbnailSize
|
||
|
contentMode:PHImageContentModeAspectFit
|
||
|
options:nil
|
||
|
resultHandler:^(UIImage *result, NSDictionary *info) {
|
||
|
|
||
|
// Only update the thumbnail if the cell tag hasn't changed. Otherwise, the cell has been re-used.
|
||
|
if (cell.tag == currentTag) {
|
||
|
[cell.imageView setImage:result];
|
||
|
}
|
||
|
}];
|
||
|
}
|
||
|
else*/
|
||
|
{
|
||
|
//NSLog(@"Image manager: Requesting FILL image for iPhone");
|
||
|
[self.imageManager requestImageForAsset:asset
|
||
|
targetSize:AssetGridThumbnailSize
|
||
|
contentMode:PHImageContentModeAspectFill
|
||
|
options:nil
|
||
|
resultHandler:^(UIImage *result, NSDictionary *info) {
|
||
|
|
||
|
// Only update the thumbnail if the cell tag hasn't changed. Otherwise, the cell has been re-used.
|
||
|
if (cell.tag == currentTag) {
|
||
|
[cell.imageView setImage:result];
|
||
|
}
|
||
|
}];
|
||
|
}
|
||
|
|
||
|
|
||
|
[cell bind:asset];
|
||
|
|
||
|
cell.shouldShowSelection = self.picker.allowsMultipleSelection;
|
||
|
|
||
|
// Optional protocol to determine if some kind of assets can't be selected (pej long videos, etc...)
|
||
|
if ([self.picker.delegate respondsToSelector:@selector(assetsPickerController:shouldEnableAsset:)]) {
|
||
|
cell.enabled = [self.picker.delegate assetsPickerController:self.picker shouldEnableAsset:asset];
|
||
|
} else {
|
||
|
cell.enabled = YES;
|
||
|
}
|
||
|
|
||
|
// 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;
|
||
|
[collectionView selectItemAtIndexPath:indexPath animated:NO scrollPosition:UICollectionViewScrollPositionNone];
|
||
|
} else {
|
||
|
cell.selected = NO;
|
||
|
}
|
||
|
|
||
|
return cell;
|
||
|
}
|
||
|
|
||
|
|
||
|
#pragma mark - Collection View Delegate
|
||
|
|
||
|
- (BOOL)collectionView:(UICollectionView *)collectionView shouldSelectItemAtIndexPath:(NSIndexPath *)indexPath
|
||
|
{
|
||
|
PHAsset *asset = self.assetsFetchResults[(NSUInteger)indexPath.item];
|
||
|
|
||
|
GMGridViewCell *cell = (GMGridViewCell *)[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.assetsFetchResults[(NSUInteger)indexPath.item];
|
||
|
|
||
|
[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.assetsFetchResults[(NSUInteger)indexPath.item];
|
||
|
|
||
|
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.assetsFetchResults[(NSUInteger)indexPath.item];
|
||
|
|
||
|
[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.assetsFetchResults[(NSUInteger)indexPath.item];
|
||
|
|
||
|
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.assetsFetchResults[(NSUInteger)indexPath.item];
|
||
|
|
||
|
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.assetsFetchResults[(NSUInteger)indexPath.item];
|
||
|
|
||
|
if ([self.picker.delegate respondsToSelector:@selector(assetsPickerController:didUnhighlightAsset:)]) {
|
||
|
[self.picker.delegate assetsPickerController:self.picker didUnhighlightAsset:asset];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
#pragma mark - UICollectionViewDataSource
|
||
|
|
||
|
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
|
||
|
{
|
||
|
NSInteger count = (NSInteger)self.assetsFetchResults.count;
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
|
||
|
#pragma mark - PHPhotoLibraryChangeObserver
|
||
|
|
||
|
- (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(), ^{
|
||
|
|
||
|
// check if there are changes to the assets (insertions, deletions, updates)
|
||
|
PHFetchResultChangeDetails *collectionChanges = [changeInstance changeDetailsForFetchResult:self.assetsFetchResults];
|
||
|
if (collectionChanges) {
|
||
|
|
||
|
// get the new fetch result
|
||
|
self.assetsFetchResults = [collectionChanges fetchResultAfterChanges];
|
||
|
|
||
|
UICollectionView *collectionView = self.collectionView;
|
||
|
|
||
|
if (![collectionChanges hasIncrementalChanges] || [collectionChanges hasMoves]) {
|
||
|
// we need to reload all if the incremental diffs are not available
|
||
|
[collectionView reloadData];
|
||
|
|
||
|
} else {
|
||
|
// if we have incremental diffs, tell the collection view to animate insertions and deletions
|
||
|
[collectionView performBatchUpdates:^{
|
||
|
NSIndexSet *removedIndexes = [collectionChanges removedIndexes];
|
||
|
if ([removedIndexes count]) {
|
||
|
[collectionView deleteItemsAtIndexPaths:[removedIndexes aapl_indexPathsFromIndexesWithSection:0]];
|
||
|
}
|
||
|
NSIndexSet *insertedIndexes = [collectionChanges insertedIndexes];
|
||
|
if ([insertedIndexes count]) {
|
||
|
[collectionView insertItemsAtIndexPaths:[insertedIndexes aapl_indexPathsFromIndexesWithSection:0]];
|
||
|
if (self.picker.showCameraButton && self.picker.autoSelectCameraImages) {
|
||
|
for (NSIndexPath *path in [insertedIndexes aapl_indexPathsFromIndexesWithSection:0]) {
|
||
|
[self collectionView:collectionView didSelectItemAtIndexPath:path];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
NSIndexSet *changedIndexes = [collectionChanges changedIndexes];
|
||
|
if ([changedIndexes count]) {
|
||
|
[collectionView reloadItemsAtIndexPaths:[changedIndexes aapl_indexPathsFromIndexesWithSection:0]];
|
||
|
}
|
||
|
} completion:NULL];
|
||
|
}
|
||
|
|
||
|
[self resetCachedAssets];
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
|
||
|
#pragma mark - UIScrollViewDelegate
|
||
|
|
||
|
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
|
||
|
{
|
||
|
[self updateCachedAssets];
|
||
|
}
|
||
|
|
||
|
|
||
|
#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.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 aapl_indexPathsForElementsInRect:removedRect];
|
||
|
[removedIndexPaths addObjectsFromArray:indexPaths];
|
||
|
} addedHandler:^(CGRect addedRect) {
|
||
|
NSArray *indexPaths = [self.collectionView aapl_indexPathsForElementsInRect:addedRect];
|
||
|
[addedIndexPaths addObjectsFromArray:indexPaths];
|
||
|
}];
|
||
|
|
||
|
NSArray *assetsToStartCaching = [self assetsAtIndexPaths:addedIndexPaths];
|
||
|
NSArray *assetsToStopCaching = [self assetsAtIndexPaths:removedIndexPaths];
|
||
|
|
||
|
[self.imageManager startCachingImagesForAssets:assetsToStartCaching
|
||
|
targetSize:AssetGridThumbnailSize
|
||
|
contentMode:PHImageContentModeAspectFill
|
||
|
options:nil];
|
||
|
[self.imageManager stopCachingImagesForAssets:assetsToStopCaching
|
||
|
targetSize:AssetGridThumbnailSize
|
||
|
contentMode:PHImageContentModeAspectFill
|
||
|
options:nil];
|
||
|
|
||
|
self.previousPreheatRect = preheatRect;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
- (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);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
- (NSArray *)assetsAtIndexPaths:(NSArray *)indexPaths
|
||
|
{
|
||
|
if (indexPaths.count == 0) { return nil; }
|
||
|
|
||
|
NSMutableArray *assets = [NSMutableArray arrayWithCapacity:indexPaths.count];
|
||
|
for (NSIndexPath *indexPath in indexPaths) {
|
||
|
PHAsset *asset = self.assetsFetchResults[(NSUInteger)indexPath.item];
|
||
|
[assets addObject:asset];
|
||
|
}
|
||
|
return assets;
|
||
|
}
|
||
|
|
||
|
|
||
|
@end
|