mirror of
https://github.com/status-im/react-native-image-crop-picker.git
synced 2025-02-23 02:48:12 +00:00
Merge pull request #68 from superandrew213/original-without-loader
Add smart album option iOS only
This commit is contained in:
commit
a67b067c86
@ -69,6 +69,7 @@ ImagePicker.clean().then(() => {
|
||||
| includeBase64 | bool (default false) | Enable or disable returning base64 data with image |
|
||||
| maxFiles (ios only) | number (default 5) | Max number of files to select when using `multiple` option |
|
||||
| compressVideo (ios only) | number (default true) | When video is selected, compress it and convert it to mp4 |
|
||||
| smartAlbums (ios only) | array (default ['UserLibrary', 'PhotoStream', 'Panoramas', 'Videos', 'Bursts']) |
|
||||
|
||||
#### Response Object
|
||||
|
||||
|
@ -42,7 +42,7 @@ RCT_EXPORT_MODULE();
|
||||
@"height": @200
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@ -65,7 +65,7 @@ RCT_EXPORT_MODULE();
|
||||
- (void) setConfiguration:(NSDictionary *)options
|
||||
resolver:(RCTPromiseResolveBlock)resolve
|
||||
rejecter:(RCTPromiseRejectBlock)reject {
|
||||
|
||||
|
||||
self.resolve = resolve;
|
||||
self.reject = reject;
|
||||
self.options = [NSMutableDictionary dictionaryWithDictionary:self.defaultOptions];
|
||||
@ -79,16 +79,16 @@ RCT_EXPORT_MODULE();
|
||||
while (root.presentedViewController != nil) {
|
||||
root = root.presentedViewController;
|
||||
}
|
||||
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(openCamera:(NSDictionary *)options
|
||||
resolver:(RCTPromiseResolveBlock)resolve
|
||||
rejecter:(RCTPromiseRejectBlock)reject) {
|
||||
|
||||
|
||||
[self setConfiguration:options resolver:resolve rejecter:reject];
|
||||
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR
|
||||
self.reject(ERROR_PICKER_CANNOT_RUN_CAMERA_ON_SIMULATOR_KEY, ERROR_PICKER_CANNOT_RUN_CAMERA_ON_SIMULATOR_MSG, nil);
|
||||
return;
|
||||
@ -98,13 +98,13 @@ RCT_EXPORT_METHOD(openCamera:(NSDictionary *)options
|
||||
self.reject(ERROR_PICKER_NO_CAMERA_PERMISSION_KEY, ERROR_PICKER_NO_CAMERA_PERMISSION_MSG, nil);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
UIImagePickerController *picker = [[UIImagePickerController alloc] init];
|
||||
picker.delegate = self;
|
||||
picker.allowsEditing = NO;
|
||||
picker.sourceType = UIImagePickerControllerSourceTypeCamera;
|
||||
picker.delegate = self;
|
||||
|
||||
|
||||
[[self getRootVC] presentViewController:picker animated:YES completion:nil];
|
||||
}];
|
||||
#endif
|
||||
@ -123,38 +123,38 @@ RCT_EXPORT_METHOD(openCamera:(NSDictionary *)options
|
||||
- (NSString*) getTmpDirectory {
|
||||
NSString *TMP_DIRECTORY = @"react-native-image-crop-picker/";
|
||||
NSString *tmpFullPath = [NSTemporaryDirectory() stringByAppendingString:TMP_DIRECTORY];
|
||||
|
||||
|
||||
BOOL isDir;
|
||||
BOOL exists = [[NSFileManager defaultManager] fileExistsAtPath:tmpFullPath isDirectory:&isDir];
|
||||
if (!exists) {
|
||||
[[NSFileManager defaultManager] createDirectoryAtPath: tmpFullPath
|
||||
withIntermediateDirectories:YES attributes:nil error:nil];
|
||||
}
|
||||
|
||||
|
||||
return tmpFullPath;
|
||||
}
|
||||
|
||||
- (BOOL)cleanTmpDirectory {
|
||||
NSString* tmpDirectoryPath = [self getTmpDirectory];
|
||||
NSArray* tmpDirectory = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:tmpDirectoryPath error:NULL];
|
||||
|
||||
|
||||
for (NSString *file in tmpDirectory) {
|
||||
BOOL deleted = [[NSFileManager defaultManager] removeItemAtPath:[NSString stringWithFormat:@"%@%@", tmpDirectoryPath, file] error:NULL];
|
||||
|
||||
|
||||
if (!deleted) {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(cleanSingle:(NSString *) path
|
||||
resolver:(RCTPromiseResolveBlock)resolve
|
||||
rejecter:(RCTPromiseRejectBlock)reject) {
|
||||
|
||||
|
||||
BOOL deleted = [[NSFileManager defaultManager] removeItemAtPath:path error:NULL];
|
||||
|
||||
|
||||
if (!deleted) {
|
||||
reject(ERROR_CLEANUP_ERROR_KEY, ERROR_CLEANUP_ERROR_MSG, nil);
|
||||
} else {
|
||||
@ -174,7 +174,7 @@ RCT_REMAP_METHOD(clean, resolver:(RCTPromiseResolveBlock)resolve
|
||||
RCT_EXPORT_METHOD(openPicker:(NSDictionary *)options
|
||||
resolver:(RCTPromiseResolveBlock)resolve
|
||||
rejecter:(RCTPromiseRejectBlock)reject) {
|
||||
|
||||
|
||||
[self setConfiguration:options resolver:resolve rejecter:reject];
|
||||
[PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
@ -185,13 +185,30 @@ RCT_EXPORT_METHOD(openPicker:(NSDictionary *)options
|
||||
imagePickerController.allowsMultipleSelection = [[self.options objectForKey:@"multiple"] boolValue];
|
||||
imagePickerController.maximumNumberOfSelection = [[self.options objectForKey:@"maxFiles"] intValue];
|
||||
imagePickerController.showsNumberOfSelectedAssets = YES;
|
||||
|
||||
|
||||
if ([self.options objectForKey:@"smartAlbums"] != nil) {
|
||||
NSDictionary *smartAlbums = @{
|
||||
@"UserLibrary" : @(PHAssetCollectionSubtypeSmartAlbumUserLibrary),
|
||||
@"PhotoStream" : @(PHAssetCollectionSubtypeAlbumMyPhotoStream),
|
||||
@"Panoramas" : @(PHAssetCollectionSubtypeSmartAlbumPanoramas),
|
||||
@"Videos" : @(PHAssetCollectionSubtypeSmartAlbumVideos),
|
||||
@"Bursts" : @(PHAssetCollectionSubtypeSmartAlbumBursts),
|
||||
};
|
||||
NSMutableArray *albumsToShow = [NSMutableArray arrayWithCapacity:5];
|
||||
for (NSString* album in [self.options objectForKey:@"smartAlbums"]) {
|
||||
if ([smartAlbums objectForKey:album] != nil) {
|
||||
[albumsToShow addObject:[smartAlbums objectForKey:album]];
|
||||
}
|
||||
}
|
||||
imagePickerController.assetCollectionSubtypes = albumsToShow;
|
||||
}
|
||||
|
||||
if ([[self.options objectForKey:@"cropping"] boolValue]) {
|
||||
imagePickerController.mediaType = QBImagePickerMediaTypeImage;
|
||||
} else {
|
||||
imagePickerController.mediaType = QBImagePickerMediaTypeAny;
|
||||
}
|
||||
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[[self getRootVC] presentViewController:imagePickerController animated:YES completion:nil];
|
||||
});
|
||||
@ -215,18 +232,18 @@ RCT_EXPORT_METHOD(openPicker:(NSDictionary *)options
|
||||
|
||||
- (void)showActivityIndicator:(void (^)(UIActivityIndicatorView*, UIView*))handler {
|
||||
UIView *mainView = [[self getRootVC] view];
|
||||
|
||||
|
||||
// create overlay
|
||||
UIView *loadingView = [[UIView alloc] initWithFrame:[UIScreen mainScreen].bounds];
|
||||
loadingView.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.5];
|
||||
loadingView.clipsToBounds = YES;
|
||||
|
||||
|
||||
// create loading spinner
|
||||
UIActivityIndicatorView *activityView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
|
||||
activityView.frame = CGRectMake(65, 40, activityView.bounds.size.width, activityView.bounds.size.height);
|
||||
activityView.center = loadingView.center;
|
||||
[loadingView addSubview:activityView];
|
||||
|
||||
|
||||
// create message
|
||||
UILabel *loadingLabel = [[UILabel alloc] initWithFrame:CGRectMake(20, 115, 130, 22)];
|
||||
loadingLabel.backgroundColor = [UIColor clearColor];
|
||||
@ -239,11 +256,11 @@ RCT_EXPORT_METHOD(openPicker:(NSDictionary *)options
|
||||
loadingLabel.text = @"Processing assets...";
|
||||
[loadingLabel setFont:[UIFont boldSystemFontOfSize:18]];
|
||||
[loadingView addSubview:loadingLabel];
|
||||
|
||||
|
||||
// show all
|
||||
[mainView addSubview:loadingView];
|
||||
[activityView startAnimating];
|
||||
|
||||
|
||||
handler(activityView, loadingView);
|
||||
}
|
||||
|
||||
@ -252,7 +269,7 @@ RCT_EXPORT_METHOD(openPicker:(NSDictionary *)options
|
||||
PHImageManager *manager = [PHImageManager defaultManager];
|
||||
PHVideoRequestOptions *options = [[PHVideoRequestOptions alloc] init];
|
||||
options.version = PHVideoRequestOptionsVersionOriginal;
|
||||
|
||||
|
||||
[manager
|
||||
requestAVAssetForVideo:forAsset
|
||||
options:options
|
||||
@ -261,13 +278,13 @@ RCT_EXPORT_METHOD(openPicker:(NSDictionary *)options
|
||||
NSURL *sourceURL = [(AVURLAsset *)asset URL];
|
||||
AVAssetTrack *track = [[asset tracksWithMediaType:AVMediaTypeVideo] firstObject];
|
||||
CGSize dimensions = CGSizeApplyAffineTransform(track.naturalSize, track.preferredTransform);
|
||||
|
||||
|
||||
if (![[self.options objectForKey:@"compressVideo"] boolValue]) {
|
||||
NSNumber *fileSizeValue = nil;
|
||||
[sourceURL getResourceValue:&fileSizeValue
|
||||
forKey:NSURLFileSizeKey
|
||||
error:nil];
|
||||
|
||||
|
||||
completion([self createAttachmentResponse:[sourceURL absoluteString]
|
||||
withWidth:[NSNumber numberWithFloat:dimensions.width]
|
||||
withHeight:[NSNumber numberWithFloat:dimensions.height]
|
||||
@ -276,20 +293,20 @@ RCT_EXPORT_METHOD(openPicker:(NSDictionary *)options
|
||||
withData:[NSNull null]]);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// create temp file
|
||||
NSString *tmpDirFullPath = [self getTmpDirectory];
|
||||
NSString *filePath = [tmpDirFullPath stringByAppendingString:[[NSUUID UUID] UUIDString]];
|
||||
filePath = [filePath stringByAppendingString:@".mp4"];
|
||||
NSURL *outputURL = [NSURL fileURLWithPath:filePath];
|
||||
|
||||
|
||||
[self convertVideoToLowQuailtyWithInputURL:sourceURL outputURL:outputURL handler:^(AVAssetExportSession *exportSession) {
|
||||
if (exportSession.status == AVAssetExportSessionStatusCompleted) {
|
||||
NSNumber *fileSizeValue = nil;
|
||||
[outputURL getResourceValue:&fileSizeValue
|
||||
forKey:NSURLFileSizeKey
|
||||
error:nil];
|
||||
|
||||
|
||||
completion([self createAttachmentResponse:[outputURL absoluteString]
|
||||
withWidth:[NSNumber numberWithFloat:dimensions.width]
|
||||
withHeight:[NSNumber numberWithFloat:dimensions.height]
|
||||
@ -317,31 +334,31 @@ RCT_EXPORT_METHOD(openPicker:(NSDictionary *)options
|
||||
- (void)qb_imagePickerController:
|
||||
(QBImagePickerController *)imagePickerController
|
||||
didFinishPickingAssets:(NSArray *)assets {
|
||||
|
||||
|
||||
PHImageManager *manager = [PHImageManager defaultManager];
|
||||
|
||||
|
||||
if ([[[self options] objectForKey:@"multiple"] boolValue]) {
|
||||
NSMutableArray *selections = [[NSMutableArray alloc] init];
|
||||
PHImageRequestOptions* options = [[PHImageRequestOptions alloc] init];
|
||||
options.synchronous = YES;
|
||||
|
||||
|
||||
[self showActivityIndicator:^(UIActivityIndicatorView *indicatorView, UIView *overlayView) {
|
||||
NSLock *lock = [[NSLock alloc] init];
|
||||
__block int processed = 0;
|
||||
|
||||
|
||||
for (PHAsset *phAsset in assets) {
|
||||
|
||||
|
||||
if (phAsset.mediaType == PHAssetMediaTypeVideo) {
|
||||
[self getVideoAsset:phAsset completion:^(NSDictionary* video) {
|
||||
[lock lock];
|
||||
|
||||
|
||||
if (video != nil) {
|
||||
[selections addObject:video];
|
||||
}
|
||||
|
||||
|
||||
processed++;
|
||||
[lock unlock];
|
||||
|
||||
|
||||
if (processed == [assets count]) {
|
||||
self.resolve(selections);
|
||||
[indicatorView stopAnimating];
|
||||
@ -357,14 +374,14 @@ RCT_EXPORT_METHOD(openPicker:(NSDictionary *)options
|
||||
resultHandler:^(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info) {
|
||||
UIImage *image = [UIImage imageWithData:imageData];
|
||||
NSData *data = UIImageJPEGRepresentation(image, 1);
|
||||
|
||||
|
||||
NSString *filePath = [self persistFile:data];
|
||||
if (filePath == nil) {
|
||||
self.reject(ERROR_CANNOT_SAVE_IMAGE_KEY, ERROR_CANNOT_SAVE_IMAGE_MSG, nil);
|
||||
[imagePickerController dismissViewControllerAnimated:YES completion:nil];
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
[lock lock];
|
||||
[selections addObject:[self createAttachmentResponse:filePath
|
||||
withWidth:@(phAsset.pixelWidth)
|
||||
@ -375,7 +392,7 @@ RCT_EXPORT_METHOD(openPicker:(NSDictionary *)options
|
||||
]];
|
||||
processed++;
|
||||
[lock unlock];
|
||||
|
||||
|
||||
if (processed == [assets count]) {
|
||||
self.resolve(selections);
|
||||
[indicatorView stopAnimating];
|
||||
@ -389,9 +406,9 @@ RCT_EXPORT_METHOD(openPicker:(NSDictionary *)options
|
||||
}];
|
||||
} else {
|
||||
PHAsset *phAsset = [assets objectAtIndex:0];
|
||||
|
||||
|
||||
if (phAsset.mediaType == PHAssetMediaTypeVideo) {
|
||||
|
||||
|
||||
[self showActivityIndicator:^(UIActivityIndicatorView *indicatorView, UIView *overlayView) {
|
||||
[self getVideoAsset:phAsset completion:^(NSDictionary* video) {
|
||||
if (video != nil) {
|
||||
@ -399,8 +416,8 @@ RCT_EXPORT_METHOD(openPicker:(NSDictionary *)options
|
||||
} else {
|
||||
self.reject(ERROR_CANNOT_PROCESS_VIDEO_KEY, ERROR_CANNOT_PROCESS_VIDEO_MSG, nil);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
[indicatorView stopAnimating];
|
||||
[overlayView removeFromSuperview];
|
||||
[imagePickerController dismissViewControllerAnimated:YES completion:nil];
|
||||
@ -430,11 +447,11 @@ RCT_EXPORT_METHOD(openPicker:(NSDictionary *)options
|
||||
- (void) processSingleImagePick:(UIImage*)image withViewController:(UIViewController*)viewController {
|
||||
if ([[[self options] objectForKey:@"cropping"] boolValue]) {
|
||||
RSKImageCropViewController *imageCropVC = [[RSKImageCropViewController alloc] initWithImage:image cropMode:RSKImageCropModeCustom];
|
||||
|
||||
|
||||
imageCropVC.avoidEmptySpaceAroundImage = YES;
|
||||
imageCropVC.dataSource = self;
|
||||
imageCropVC.delegate = self;
|
||||
|
||||
|
||||
[viewController dismissViewControllerAnimated:YES completion:^{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[[self getRootVC] presentViewController:imageCropVC animated:YES completion:nil];
|
||||
@ -448,14 +465,14 @@ RCT_EXPORT_METHOD(openPicker:(NSDictionary *)options
|
||||
[viewController dismissViewControllerAnimated:YES completion:nil];
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
self.resolve([self createAttachmentResponse:filePath
|
||||
withWidth:@(image.size.width)
|
||||
withHeight:@(image.size.height)
|
||||
withMime:@"image/jpeg"
|
||||
withSize:[NSNumber numberWithUnsignedInteger:data.length]
|
||||
withData:[[self.options objectForKey:@"includeBase64"] boolValue] ? [data base64EncodedStringWithOptions:0] : [NSNull null]]);
|
||||
|
||||
|
||||
[viewController dismissViewControllerAnimated:YES completion:nil];
|
||||
}
|
||||
}
|
||||
@ -468,14 +485,14 @@ RCT_EXPORT_METHOD(openPicker:(NSDictionary *)options
|
||||
CGSize maskSize = CGSizeMake(
|
||||
[[self.options objectForKey:@"width"] intValue],
|
||||
[[self.options objectForKey:@"height"] intValue]);
|
||||
|
||||
|
||||
CGFloat viewWidth = CGRectGetWidth(controller.view.frame);
|
||||
CGFloat viewHeight = CGRectGetHeight(controller.view.frame);
|
||||
|
||||
|
||||
CGRect maskRect = CGRectMake((viewWidth - maskSize.width) * 0.5f,
|
||||
(viewHeight - maskSize.height) * 0.5f,
|
||||
maskSize.width, maskSize.height);
|
||||
|
||||
|
||||
return maskRect;
|
||||
}
|
||||
|
||||
@ -485,13 +502,13 @@ RCT_EXPORT_METHOD(openPicker:(NSDictionary *)options
|
||||
CGRect rect = controller.maskRect;
|
||||
CGFloat viewWidth = CGRectGetWidth(controller.view.frame);
|
||||
CGFloat viewHeight = CGRectGetHeight(controller.view.frame);
|
||||
|
||||
|
||||
double scaleFactor = fmin(viewWidth / rect.size.width, viewHeight / rect.size.height);
|
||||
rect.size.width *= scaleFactor;
|
||||
rect.size.height *= scaleFactor;
|
||||
rect.origin.x = (viewWidth - rect.size.width) / 2;
|
||||
rect.origin.y = (viewHeight - rect.size.height) / 2;
|
||||
|
||||
|
||||
return rect;
|
||||
}
|
||||
|
||||
@ -524,27 +541,27 @@ RCT_EXPORT_METHOD(openPicker:(NSDictionary *)options
|
||||
- (void)imageCropViewController:(RSKImageCropViewController *)controller
|
||||
didCropImage:(UIImage *)croppedImage
|
||||
usingCropRect:(CGRect)cropRect {
|
||||
|
||||
|
||||
// we have correct rect, but not correct dimensions
|
||||
// so resize image
|
||||
CGSize resizedImageSize = CGSizeMake([[[self options] objectForKey:@"width"] intValue], [[[self options] objectForKey:@"height"] intValue]);
|
||||
UIImage *resizedImage = [croppedImage resizedImageToFitInSize:resizedImageSize scaleIfSmaller:YES];
|
||||
NSData *data = UIImageJPEGRepresentation(resizedImage, 1);
|
||||
|
||||
|
||||
NSString *filePath = [self persistFile:data];
|
||||
if (filePath == nil) {
|
||||
self.reject(ERROR_CANNOT_SAVE_IMAGE_KEY, ERROR_CANNOT_SAVE_IMAGE_MSG, nil);
|
||||
[controller dismissViewControllerAnimated:YES completion:nil];
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
self.resolve([self createAttachmentResponse:filePath
|
||||
withWidth:@(resizedImage.size.width)
|
||||
withHeight:@(resizedImage.size.height)
|
||||
withMime:@"image/jpeg"
|
||||
withSize:[NSNumber numberWithUnsignedInteger:data.length]
|
||||
withData:[[self.options objectForKey:@"includeBase64"] boolValue] ? [data base64EncodedStringWithOptions:0] : [NSNull null]]);
|
||||
|
||||
|
||||
[controller dismissViewControllerAnimated:YES completion:nil];
|
||||
}
|
||||
|
||||
@ -555,13 +572,13 @@ RCT_EXPORT_METHOD(openPicker:(NSDictionary *)options
|
||||
NSString *tmpDirFullPath = [self getTmpDirectory];
|
||||
NSString *filePath = [tmpDirFullPath stringByAppendingString:[[NSUUID UUID] UUIDString]];
|
||||
filePath = [filePath stringByAppendingString:@".jpg"];
|
||||
|
||||
|
||||
// save cropped file
|
||||
BOOL status = [data writeToFile:filePath atomically:YES];
|
||||
if (!status) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
return filePath;
|
||||
}
|
||||
|
||||
|
@ -43,7 +43,7 @@ static CGSize CGSizeScale(CGSize size, CGFloat scale) {
|
||||
{
|
||||
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;
|
||||
@ -73,10 +73,10 @@ static CGSize CGSizeScale(CGSize size, CGFloat scale) {
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
|
||||
|
||||
[self setUpToolbarItems];
|
||||
[self resetCachedAssets];
|
||||
|
||||
|
||||
// Register observer
|
||||
[[PHPhotoLibrary sharedPhotoLibrary] registerChangeObserver:self];
|
||||
}
|
||||
@ -84,25 +84,25 @@ static CGSize CGSizeScale(CGSize size, CGFloat scale) {
|
||||
- (void)viewWillAppear:(BOOL)animated
|
||||
{
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
|
||||
// Configure navigation item
|
||||
self.navigationItem.title = self.assetCollection.localizedTitle;
|
||||
self.navigationItem.prompt = self.imagePickerController.prompt;
|
||||
|
||||
|
||||
// Configure collection view
|
||||
self.collectionView.allowsMultipleSelection = self.imagePickerController.allowsMultipleSelection;
|
||||
|
||||
|
||||
// Show/hide 'Done' button
|
||||
if (self.imagePickerController.allowsMultipleSelection) {
|
||||
[self.navigationItem setRightBarButtonItem:self.doneButton animated:NO];
|
||||
} else {
|
||||
[self.navigationItem setRightBarButtonItem:nil animated:NO];
|
||||
}
|
||||
|
||||
|
||||
[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];
|
||||
@ -113,16 +113,16 @@ static CGSize CGSizeScale(CGSize size, CGFloat scale) {
|
||||
- (void)viewWillDisappear:(BOOL)animated
|
||||
{
|
||||
[super viewWillDisappear:animated];
|
||||
|
||||
|
||||
self.disableScrollToBottom = YES;
|
||||
}
|
||||
|
||||
- (void)viewDidAppear:(BOOL)animated
|
||||
{
|
||||
[super viewDidAppear:animated];
|
||||
|
||||
|
||||
self.disableScrollToBottom = NO;
|
||||
|
||||
|
||||
[self updateCachedAssets];
|
||||
}
|
||||
|
||||
@ -130,10 +130,10 @@ static CGSize CGSizeScale(CGSize size, CGFloat scale) {
|
||||
{
|
||||
// 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<UIViewControllerTransitionCoordinatorContext> context) {
|
||||
[self.collectionView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionBottom animated:NO];
|
||||
@ -152,7 +152,7 @@ static CGSize CGSizeScale(CGSize size, CGFloat scale) {
|
||||
- (void)setAssetCollection:(PHAssetCollection *)assetCollection
|
||||
{
|
||||
_assetCollection = assetCollection;
|
||||
|
||||
|
||||
[self updateFetchRequest];
|
||||
[self.collectionView reloadData];
|
||||
}
|
||||
@ -162,7 +162,7 @@ static CGSize CGSizeScale(CGSize size, CGFloat scale) {
|
||||
if (_imageManager == nil) {
|
||||
_imageManager = [PHCachingImageManager new];
|
||||
}
|
||||
|
||||
|
||||
return _imageManager;
|
||||
}
|
||||
|
||||
@ -179,7 +179,7 @@ static CGSize CGSizeScale(CGSize size, CGFloat scale) {
|
||||
{
|
||||
if ([self.imagePickerController.delegate respondsToSelector:@selector(qb_imagePickerController:didFinishPickingAssets:)]) {
|
||||
[self.imagePickerController.delegate qb_imagePickerController:self.imagePickerController
|
||||
didFinishPickingAssets:self.imagePickerController.selectedAssets.array];
|
||||
didFinishPickingAssets:self.imagePickerController.selectedAssets.array];
|
||||
}
|
||||
}
|
||||
|
||||
@ -191,21 +191,21 @@ static CGSize CGSizeScale(CGSize size, CGFloat scale) {
|
||||
// Space
|
||||
UIBarButtonItem *leftSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:NULL];
|
||||
UIBarButtonItem *rightSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:NULL];
|
||||
|
||||
|
||||
// Info label
|
||||
NSDictionary *attributes = @{ NSForegroundColorAttributeName: [UIColor blackColor] };
|
||||
UIBarButtonItem *infoButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStylePlain target:nil action:NULL];
|
||||
infoButtonItem.enabled = NO;
|
||||
[infoButtonItem setTitleTextAttributes:attributes forState:UIControlStateNormal];
|
||||
[infoButtonItem setTitleTextAttributes:attributes forState:UIControlStateDisabled];
|
||||
|
||||
|
||||
self.toolbarItems = @[leftSpace, infoButtonItem, rightSpace];
|
||||
}
|
||||
|
||||
- (void)updateSelectionInfo
|
||||
{
|
||||
NSMutableOrderedSet *selectedAssets = self.imagePickerController.selectedAssets;
|
||||
|
||||
|
||||
if (selectedAssets.count > 0) {
|
||||
NSBundle *bundle = self.imagePickerController.assetBundle;
|
||||
NSString *format;
|
||||
@ -214,7 +214,7 @@ static CGSize CGSizeScale(CGSize size, CGFloat scale) {
|
||||
} else {
|
||||
format = NSLocalizedStringFromTableInBundle(@"assets.toolbar.item-selected", @"QBImagePicker", bundle, nil);
|
||||
}
|
||||
|
||||
|
||||
NSString *title = [NSString stringWithFormat:format, selectedAssets.count];
|
||||
[(UIBarButtonItem *)self.toolbarItems[1] setTitle:title];
|
||||
} else {
|
||||
@ -229,22 +229,22 @@ static CGSize CGSizeScale(CGSize size, CGFloat scale) {
|
||||
{
|
||||
if (self.assetCollection) {
|
||||
PHFetchOptions *options = [PHFetchOptions new];
|
||||
|
||||
|
||||
switch (self.imagePickerController.mediaType) {
|
||||
case QBImagePickerMediaTypeImage:
|
||||
options.predicate = [NSPredicate predicateWithFormat:@"mediaType == %ld", PHAssetMediaTypeImage];
|
||||
break;
|
||||
|
||||
|
||||
case QBImagePickerMediaTypeVideo:
|
||||
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];
|
||||
@ -267,11 +267,11 @@ static CGSize CGSizeScale(CGSize size, CGFloat scale) {
|
||||
- (BOOL)isMaximumSelectionLimitReached
|
||||
{
|
||||
NSUInteger minimumNumberOfSelection = MAX(1, self.imagePickerController.minimumNumberOfSelection);
|
||||
|
||||
|
||||
if (minimumNumberOfSelection <= self.imagePickerController.maximumNumberOfSelection) {
|
||||
return (self.imagePickerController.maximumNumberOfSelection <= self.imagePickerController.selectedAssets.count);
|
||||
}
|
||||
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
@ -293,19 +293,19 @@ static CGSize CGSizeScale(CGSize size, CGFloat scale) {
|
||||
{
|
||||
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];
|
||||
@ -313,13 +313,13 @@ static CGSize CGSizeScale(CGSize size, CGFloat scale) {
|
||||
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, [[UIScreen mainScreen] scale]);
|
||||
|
||||
|
||||
[self.imageManager startCachingImagesForAssets:assetsToStartCaching
|
||||
targetSize:targetSize
|
||||
contentMode:PHImageContentModeAspectFill
|
||||
@ -328,7 +328,7 @@ static CGSize CGSizeScale(CGSize size, CGFloat scale) {
|
||||
targetSize:targetSize
|
||||
contentMode:PHImageContentModeAspectFill
|
||||
options:nil];
|
||||
|
||||
|
||||
self.previousPreheatRect = preheatRect;
|
||||
}
|
||||
}
|
||||
@ -340,7 +340,7 @@ static CGSize CGSizeScale(CGSize size, CGFloat scale) {
|
||||
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);
|
||||
@ -366,7 +366,7 @@ static CGSize CGSizeScale(CGSize size, CGFloat scale) {
|
||||
- (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) {
|
||||
@ -384,11 +384,11 @@ static CGSize CGSizeScale(CGSize size, CGFloat scale) {
|
||||
{
|
||||
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 hasIncrementalChanges] || [collectionChanges hasMoves]) {
|
||||
// We need to reload all if the incremental diffs are not available
|
||||
[self.collectionView reloadData];
|
||||
@ -399,19 +399,19 @@ static CGSize CGSizeScale(CGSize size, CGFloat scale) {
|
||||
if ([removedIndexes count]) {
|
||||
[self.collectionView deleteItemsAtIndexPaths:[removedIndexes qb_indexPathsFromIndexesWithSection:0]];
|
||||
}
|
||||
|
||||
|
||||
NSIndexSet *insertedIndexes = [collectionChanges insertedIndexes];
|
||||
if ([insertedIndexes count]) {
|
||||
[self.collectionView insertItemsAtIndexPaths:[insertedIndexes qb_indexPathsFromIndexesWithSection:0]];
|
||||
}
|
||||
|
||||
|
||||
NSIndexSet *changedIndexes = [collectionChanges changedIndexes];
|
||||
if ([changedIndexes count]) {
|
||||
[self.collectionView reloadItemsAtIndexPaths:[changedIndexes qb_indexPathsFromIndexesWithSection:0]];
|
||||
}
|
||||
} completion:NULL];
|
||||
}
|
||||
|
||||
|
||||
[self resetCachedAssets];
|
||||
}
|
||||
});
|
||||
@ -443,12 +443,12 @@ static CGSize CGSizeScale(CGSize size, CGFloat scale) {
|
||||
QBAssetCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"AssetCell" forIndexPath:indexPath];
|
||||
cell.tag = indexPath.item;
|
||||
cell.showsOverlayViewWhenSelected = self.imagePickerController.allowsMultipleSelection;
|
||||
|
||||
|
||||
// Image
|
||||
PHAsset *asset = self.fetchResult[indexPath.item];
|
||||
CGSize itemSize = [(UICollectionViewFlowLayout *)collectionView.collectionViewLayout itemSize];
|
||||
CGSize targetSize = CGSizeScale(itemSize, [[UIScreen mainScreen] scale]);
|
||||
|
||||
|
||||
[self.imageManager requestImageForAsset:asset
|
||||
targetSize:targetSize
|
||||
contentMode:PHImageContentModeAspectFill
|
||||
@ -458,15 +458,15 @@ static CGSize CGSizeScale(CGSize size, CGFloat scale) {
|
||||
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;
|
||||
@ -478,13 +478,13 @@ static CGSize CGSizeScale(CGSize size, CGFloat scale) {
|
||||
} 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;
|
||||
}
|
||||
|
||||
@ -494,14 +494,14 @@ static CGSize CGSizeScale(CGSize size, CGFloat scale) {
|
||||
UICollectionReusableView *footerView = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionFooter
|
||||
withReuseIdentifier:@"FooterView"
|
||||
forIndexPath:indexPath];
|
||||
|
||||
|
||||
// Number of assets
|
||||
UILabel *label = (UILabel *)[footerView viewWithTag:1];
|
||||
|
||||
|
||||
NSBundle *bundle = self.imagePickerController.assetBundle;
|
||||
NSUInteger numberOfPhotos = [self.fetchResult countOfAssetsWithMediaType:PHAssetMediaTypeImage];
|
||||
NSUInteger numberOfVideos = [self.fetchResult countOfAssetsWithMediaType:PHAssetMediaTypeVideo];
|
||||
|
||||
|
||||
switch (self.imagePickerController.mediaType) {
|
||||
case QBImagePickerMediaTypeAny:
|
||||
{
|
||||
@ -517,33 +517,33 @@ static CGSize CGSizeScale(CGSize size, CGFloat scale) {
|
||||
} else {
|
||||
format = NSLocalizedStringFromTableInBundle(@"assets.footer.photos-and-videos", @"QBImagePicker", bundle, nil);
|
||||
}
|
||||
|
||||
|
||||
label.text = [NSString stringWithFormat:format, numberOfPhotos, numberOfVideos];
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case QBImagePickerMediaTypeImage:
|
||||
{
|
||||
NSString *key = (numberOfPhotos == 1) ? @"assets.footer.photo" : @"assets.footer.photos";
|
||||
NSString *format = NSLocalizedStringFromTableInBundle(key, @"QBImagePicker", bundle, nil);
|
||||
|
||||
|
||||
label.text = [NSString stringWithFormat:format, numberOfPhotos];
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case QBImagePickerMediaTypeVideo:
|
||||
{
|
||||
NSString *key = (numberOfVideos == 1) ? @"assets.footer.video" : @"assets.footer.videos";
|
||||
NSString *format = NSLocalizedStringFromTableInBundle(key, @"QBImagePicker", bundle, nil);
|
||||
|
||||
|
||||
label.text = [NSString stringWithFormat:format, numberOfVideos];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
return footerView;
|
||||
}
|
||||
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
@ -556,11 +556,11 @@ static CGSize CGSizeScale(CGSize size, CGFloat scale) {
|
||||
PHAsset *asset = self.fetchResult[indexPath.item];
|
||||
return [self.imagePickerController.delegate qb_imagePickerController:self.imagePickerController shouldSelectAsset:asset];
|
||||
}
|
||||
|
||||
|
||||
if ([self isAutoDeselectEnabled]) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
|
||||
return ![self isMaximumSelectionLimitReached];
|
||||
}
|
||||
|
||||
@ -568,30 +568,30 @@ static CGSize CGSizeScale(CGSize size, CGFloat scale) {
|
||||
{
|
||||
QBImagePickerController *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.lastSelectedItemIndexPath = indexPath;
|
||||
|
||||
|
||||
[self updateDoneButtonState];
|
||||
|
||||
|
||||
if (imagePickerController.showsNumberOfSelectedAssets) {
|
||||
[self updateSelectionInfo];
|
||||
|
||||
|
||||
if (selectedAssets.count == 1) {
|
||||
// Show toolbar
|
||||
[self.navigationController setToolbarHidden:NO animated:YES];
|
||||
@ -602,7 +602,7 @@ static CGSize CGSizeScale(CGSize size, CGFloat scale) {
|
||||
[imagePickerController.delegate qb_imagePickerController:imagePickerController didFinishPickingAssets:@[asset]];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ([imagePickerController.delegate respondsToSelector:@selector(qb_imagePickerController:didSelectAsset:)]) {
|
||||
[imagePickerController.delegate qb_imagePickerController:imagePickerController didSelectAsset:asset];
|
||||
}
|
||||
@ -613,28 +613,28 @@ static CGSize CGSizeScale(CGSize size, CGFloat scale) {
|
||||
if (!self.imagePickerController.allowsMultipleSelection) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
QBImagePickerController *imagePickerController = self.imagePickerController;
|
||||
NSMutableOrderedSet *selectedAssets = imagePickerController.selectedAssets;
|
||||
|
||||
|
||||
PHAsset *asset = self.fetchResult[indexPath.item];
|
||||
|
||||
|
||||
// Remove asset from set
|
||||
[selectedAssets removeObject:asset];
|
||||
|
||||
|
||||
self.lastSelectedItemIndexPath = nil;
|
||||
|
||||
|
||||
[self updateDoneButtonState];
|
||||
|
||||
|
||||
if (imagePickerController.showsNumberOfSelectedAssets) {
|
||||
[self updateSelectionInfo];
|
||||
|
||||
|
||||
if (selectedAssets.count == 0) {
|
||||
// Hide toolbar
|
||||
[self.navigationController setToolbarHidden:YES animated:YES];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ([imagePickerController.delegate respondsToSelector:@selector(qb_imagePickerController:didDeselectAsset:)]) {
|
||||
[imagePickerController.delegate qb_imagePickerController:imagePickerController didDeselectAsset:asset];
|
||||
}
|
||||
@ -651,9 +651,9 @@ static CGSize CGSizeScale(CGSize size, CGFloat scale) {
|
||||
} else {
|
||||
numberOfColumns = self.imagePickerController.numberOfColumnsInLandscape;
|
||||
}
|
||||
|
||||
|
||||
CGFloat width = (CGRectGetWidth(self.view.frame) - 2.0 * (numberOfColumns - 1)) / numberOfColumns;
|
||||
|
||||
|
||||
return CGSizeMake(width, width);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user