942 lines
36 KiB
Objective-C
942 lines
36 KiB
Objective-C
//
|
|
// CKGalleryViewManager.m
|
|
// ReactNativeCameraKit
|
|
//
|
|
// Created by Ran Greenberg on 20/06/2016.
|
|
// Copyright © 2016 Wix. All rights reserved.
|
|
//
|
|
|
|
@import Photos;
|
|
|
|
#import "CKGalleryViewManager.h"
|
|
#import "CKGalleryCollectionViewCell.h"
|
|
#import "CKGalleryCustomCollectionViewCell.h"
|
|
#import "GalleryData.h"
|
|
//#import "UIView+React.h"
|
|
#import "CKGalleryManager.h"
|
|
#import <MobileCoreServices/MobileCoreServices.h>
|
|
#import "CKCamera.h"
|
|
|
|
#define SCREEN_WIDTH ([[UIScreen mainScreen] bounds].size.width)
|
|
#define SCREEN_HEIGHT ([[UIScreen mainScreen] bounds].size.height)
|
|
|
|
#define DEFAULT_MINIMUM_INTERITEM_SPACING 10.0
|
|
#define DEFAULT_MINIMUM_LINE_SPACING 10.0
|
|
#define IMAGE_SIZE_MULTIPLIER 2
|
|
|
|
|
|
|
|
typedef void (^CompletionBlock)(BOOL success);
|
|
|
|
|
|
@interface CKGalleryView : UIView <UICollectionViewDelegateFlowLayout, UICollectionViewDataSource, CKGalleryCollectionViewCellDelegate>
|
|
|
|
//props
|
|
@property (nonatomic, strong) NSString *albumName;
|
|
@property (nonatomic, strong) NSNumber *minimumLineSpacing;
|
|
@property (nonatomic, strong) NSNumber *minimumInteritemSpacing;
|
|
@property (nonatomic, strong) NSNumber *columnCount;
|
|
@property (nonatomic, strong) NSNumber *getUrlOnTapImage;
|
|
@property (nonatomic, strong) NSNumber *autoSyncSelection;
|
|
@property (nonatomic, strong) NSString *imageQualityOnTap;
|
|
@property (nonatomic, copy) RCTDirectEventBlock onTapImage;
|
|
@property (nonatomic, copy) RCTDirectEventBlock onRemoteDownloadChanged;
|
|
|
|
|
|
@property (nonatomic, strong) UICollectionView *collectionView;
|
|
@property (nonatomic, strong) GalleryData *galleryData;
|
|
@property (nonatomic, strong) PHCachingImageManager *imageManager;
|
|
@property (nonatomic) CGSize cellSize;
|
|
@property (nonatomic, strong) NSMutableArray *selectedImages;
|
|
|
|
@property (nonatomic, strong) PHImageRequestOptions *imageRequestOptions;
|
|
|
|
@property (nonatomic, strong) PHFetchOptions *fetchOptions;
|
|
@property (nonatomic, strong) NSString *selectedBase64Image;
|
|
@property (nonatomic, strong) UIImage *selectedImageIcon;
|
|
@property (nonatomic, strong) UIImage *unSelectedImageIcon;
|
|
@property (nonatomic, strong) UIColor *imageStrokeColor;
|
|
@property (nonatomic, strong) NSNumber *imageStrokeColorWidth;
|
|
@property (nonatomic, strong) NSNumber *disableSelectionIcons;
|
|
@property (nonatomic, strong) NSDictionary *selection;
|
|
@property (nonatomic) UIEdgeInsets contentInset;
|
|
@property (nonatomic) BOOL alwaysBounce;
|
|
@property (nonatomic) BOOL isHorizontal;
|
|
@property (nonatomic) BOOL cellSizeInvalidated;
|
|
@property (nonatomic, strong) UIColor *remoteDownloadIndicatorColor;
|
|
@property (nonatomic, strong) NSString *remoteDownloadIndicatorType;
|
|
@property (nonatomic, strong) NSNumber *iCloudDownloadSimulateTime;
|
|
@property (nonatomic, strong) NSTimer *iCloudDownloadSimulateTimer;
|
|
@property (nonatomic) CFAbsoluteTime timePassed;
|
|
|
|
//custom button props
|
|
@property (nonatomic, strong) NSDictionary *customButtonStyle;
|
|
@property (nonatomic, copy) RCTDirectEventBlock onCustomButtonPress;
|
|
|
|
//supported props
|
|
@property (nonatomic, strong) NSDictionary *fileTypeSupport;
|
|
@property (nonatomic, strong) NSArray *supportedFileTypesArray;
|
|
|
|
@property (nonatomic, strong) RCTBridge *bridge;
|
|
|
|
@property (nonatomic) BOOL collectionViewIsScrolling;
|
|
@property (nonatomic, weak) CKGalleryCollectionViewCell *lastPressedCell;
|
|
|
|
@end
|
|
|
|
static NSString * const CellReuseIdentifier = @"Cell";
|
|
static NSString * const CustomCellReuseIdentifier = @"CustomCell";
|
|
|
|
@implementation CKGalleryView
|
|
|
|
- (instancetype)initWithFrame:(CGRect)frame {
|
|
self = [super initWithFrame:frame];
|
|
|
|
if (self) {
|
|
|
|
self.selectedImages = [[NSMutableArray alloc] init];
|
|
self.imageManager = [[PHCachingImageManager alloc] init];
|
|
|
|
self.imageRequestOptions = [[PHImageRequestOptions alloc] init];
|
|
self.imageRequestOptions.synchronous = YES;
|
|
|
|
self.contentInset = UIEdgeInsetsZero;
|
|
|
|
self.isHorizontal = NO;
|
|
self.cellSizeInvalidated = NO;
|
|
self.collectionViewIsScrolling = NO;
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
|
|
-(CGSize)cellSize {
|
|
if (CGSizeEqualToSize(_cellSize, CGSizeZero) || self.cellSizeInvalidated) {
|
|
CGFloat minSize;
|
|
CGFloat spacing = (self.columnCount.floatValue - 1.0f) * self.minimumInteritemSpacing.floatValue;
|
|
if (self.isHorizontal) {
|
|
minSize = MIN(self.bounds.size.width, (self.bounds.size.height - spacing) / self.columnCount.floatValue);
|
|
} else {
|
|
minSize = (MAX(self.bounds.size.width - spacing, 0)) / self.columnCount.floatValue;
|
|
}
|
|
_cellSize = CGSizeMake(minSize, minSize);
|
|
}
|
|
return _cellSize;
|
|
}
|
|
|
|
-(PHFetchOptions *)fetchOptions {
|
|
if (!_fetchOptions) {
|
|
PHFetchOptions *fetchOptions = [[PHFetchOptions alloc] init];
|
|
fetchOptions.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:NO]];
|
|
fetchOptions.predicate = [NSPredicate predicateWithFormat:@"mediaType = %d",PHAssetMediaTypeImage];
|
|
|
|
_fetchOptions = fetchOptions;
|
|
}
|
|
|
|
return _fetchOptions;
|
|
}
|
|
|
|
|
|
-(void)removeFromSuperview {
|
|
[CKGalleryCollectionViewCell cleanStaticsVariables];
|
|
[super removeFromSuperview];
|
|
}
|
|
|
|
|
|
-(void)reactSetFrame:(CGRect)frame {
|
|
[super reactSetFrame:frame];
|
|
|
|
if (CGRectIsEmpty(frame)) return;
|
|
|
|
if (!self.collectionView) {
|
|
self.collectionView = [[UICollectionView alloc] initWithFrame:self.bounds collectionViewLayout:[self getCollectionViewFlowLayout:self.isHorizontal]];
|
|
self.collectionView.contentInset = self.contentInset;
|
|
self.collectionView.scrollIndicatorInsets = self.contentInset;
|
|
[self handleSetAlwaysBounce:self.alwaysBounce isHorizontal:self.isHorizontal];
|
|
self.collectionView.delegate = self;
|
|
self.collectionView.dataSource = self;
|
|
|
|
[self.collectionView registerClass:[CKGalleryCollectionViewCell class] forCellWithReuseIdentifier:CellReuseIdentifier];
|
|
[self.collectionView registerClass:[CKGalleryCustomCollectionViewCell class] forCellWithReuseIdentifier:CustomCellReuseIdentifier];
|
|
[self addSubview:self.collectionView];
|
|
self.collectionView.backgroundColor = [UIColor whiteColor];
|
|
}
|
|
else {
|
|
self.collectionView.frame = self.bounds;
|
|
}
|
|
}
|
|
|
|
|
|
#pragma mark - Collection view layout things
|
|
|
|
-(UICollectionViewFlowLayout*)getCollectionViewFlowLayout:(BOOL)isHorizontal {
|
|
UICollectionViewFlowLayout* flowLayout = [[UICollectionViewFlowLayout alloc] init];
|
|
flowLayout.itemSize = self.cellSize;
|
|
[flowLayout setScrollDirection:isHorizontal ? UICollectionViewScrollDirectionHorizontal : UICollectionViewScrollDirectionVertical];
|
|
return flowLayout;
|
|
}
|
|
|
|
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
|
|
|
|
return self.cellSize;
|
|
}
|
|
|
|
|
|
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section {
|
|
return self.minimumInteritemSpacing ? self.minimumInteritemSpacing.floatValue : DEFAULT_MINIMUM_INTERITEM_SPACING;
|
|
}
|
|
|
|
|
|
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section {
|
|
return self.minimumLineSpacing ? self.minimumLineSpacing.floatValue : DEFAULT_MINIMUM_INTERITEM_SPACING;
|
|
}
|
|
|
|
-(void)upadateCollectionView:(PHFetchResult*)fetchResults animated:(BOOL)animated {
|
|
|
|
self.galleryData = [[GalleryData alloc] initWithFetchResults:fetchResults selectedImagesIds:self.selectedImages];
|
|
|
|
if (animated) {
|
|
|
|
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.4 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^ {
|
|
[self.collectionView performBatchUpdates:^{
|
|
[self.collectionView reloadSections:[NSIndexSet indexSetWithIndex:0]];
|
|
} completion:nil];
|
|
});
|
|
}
|
|
else {
|
|
dispatch_async(dispatch_get_main_queue(), ^ {
|
|
[self.collectionView reloadData];
|
|
});
|
|
}
|
|
}
|
|
|
|
|
|
-(void)setSelectedImageIcon:(UIImage *)selectedImage {
|
|
[CKGalleryCollectionViewCell setSelectedImageIcon:selectedImage];
|
|
}
|
|
|
|
|
|
-(void)setUnSelectedImageIcon:(UIImage *)unSelectedImage {
|
|
[CKGalleryCollectionViewCell setUnSlectedImageIcon:unSelectedImage];
|
|
}
|
|
|
|
-(void)setImageStrokeColor:(UIColor *)imageStrokeColor {
|
|
[CKGalleryCollectionViewCell setImageStrokeColor:imageStrokeColor];
|
|
}
|
|
|
|
-(void)setImageStrokeColorWidth:(NSNumber *)imageStrokeColorWidth {
|
|
[CKGalleryCollectionViewCell setImageStrokeColorWidth:imageStrokeColorWidth];
|
|
}
|
|
|
|
-(void)setSelection:(NSDictionary *)selection {
|
|
[CKGalleryCollectionViewCell setSelection:selection];
|
|
}
|
|
|
|
-(void)setContentInset:(UIEdgeInsets)contentInset {
|
|
if(self.collectionView) {
|
|
self.collectionView.contentInset = contentInset;
|
|
}
|
|
_contentInset = contentInset;
|
|
}
|
|
|
|
-(void)setRemoteDownloadIndicatorColor:(UIColor *)remoteDownloadIndicatorColor {
|
|
[CKGalleryCollectionViewCell setRemoteDownloadIndicatorColor:remoteDownloadIndicatorColor];
|
|
}
|
|
|
|
-(void)setRemoteDownloadIndicatorType:(NSString *)remoteDownloadIndicatorType {
|
|
[CKGalleryCollectionViewCell setRemoteDownloadIndicatorType:remoteDownloadIndicatorType];
|
|
}
|
|
|
|
-(void)handleSetAlwaysBounce:(BOOL)alwaysBounce isHorizontal:(BOOL)isHorizontal {
|
|
if (isHorizontal) {
|
|
self.collectionView.alwaysBounceHorizontal = alwaysBounce;
|
|
self.collectionView.alwaysBounceVertical = NO;
|
|
} else {
|
|
self.collectionView.alwaysBounceVertical = alwaysBounce;
|
|
self.collectionView.alwaysBounceHorizontal = NO;
|
|
}
|
|
}
|
|
|
|
-(void)setAlwaysBounce:(BOOL)alwaysBounce {
|
|
if(self.collectionView) {
|
|
[self handleSetAlwaysBounce:alwaysBounce isHorizontal:self.isHorizontal];
|
|
}
|
|
_alwaysBounce = alwaysBounce;
|
|
}
|
|
|
|
-(void)setIsHorizontal:(BOOL)isHorizontal {
|
|
_isHorizontal = isHorizontal;
|
|
if (self.collectionView) {
|
|
if ([self.collectionView.collectionViewLayout isKindOfClass:[UICollectionViewFlowLayout class]]) {
|
|
UICollectionViewFlowLayout *flowLayout = (UICollectionViewFlowLayout*)self.collectionView.collectionViewLayout;
|
|
BOOL needsLayoutSwitch = (isHorizontal && flowLayout.scrollDirection == UICollectionViewScrollDirectionVertical) ||
|
|
(!isHorizontal && flowLayout.scrollDirection == UICollectionViewScrollDirectionHorizontal);
|
|
if (needsLayoutSwitch) {
|
|
self.cellSizeInvalidated = YES;
|
|
[self handleSetAlwaysBounce:self.alwaysBounce isHorizontal:isHorizontal];
|
|
[self.collectionView reloadData];
|
|
[self.collectionView performBatchUpdates:^{
|
|
[self.collectionView.collectionViewLayout invalidateLayout];
|
|
[self.collectionView setCollectionViewLayout:[self getCollectionViewFlowLayout:isHorizontal] animated:YES];
|
|
} completion:^(BOOL finished) {
|
|
self.cellSizeInvalidated = NO;
|
|
}];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
-(void)setFileTypeSupport:(NSDictionary *)supported {
|
|
_fileTypeSupport = supported;
|
|
|
|
NSMutableDictionary *supportedDict = [[NSMutableDictionary alloc] init];
|
|
|
|
// SUPPORTED_FILE_TYPES
|
|
id supportedFileTypesId = self.fileTypeSupport[SUPPORTED_FILE_TYPES];
|
|
if (supportedFileTypesId) {
|
|
NSArray *supportedFileTypesArray = [RCTConvert NSArray:supportedFileTypesId];
|
|
supportedDict[SUPPORTED_FILE_TYPES] = supportedFileTypesArray;
|
|
|
|
self.supportedFileTypesArray = [NSArray arrayWithArray:supportedFileTypesArray];
|
|
}
|
|
|
|
// UNSUPPORTED_OVERLAY_COLOR
|
|
id unsupportedOverlayColorId = self.fileTypeSupport[UNSUPPORTED_OVERLAY_COLOR];
|
|
if (unsupportedOverlayColorId) {
|
|
UIColor *unsupportedOverlayColor = [RCTConvert UIColor:unsupportedOverlayColorId];
|
|
supportedDict[UNSUPPORTED_OVERLAY_COLOR] = unsupportedOverlayColor;
|
|
}
|
|
|
|
// UNSUPPORTED_OVERLAY_COLOR
|
|
id unsupportedImageId = self.fileTypeSupport[UNSUPPORTED_IMAGE];
|
|
if (unsupportedImageId) {
|
|
UIImage *unsupportedImage = [RCTConvert UIImage:unsupportedImageId];
|
|
supportedDict[UNSUPPORTED_IMAGE] = unsupportedImage;
|
|
}
|
|
|
|
// UNSUPPORTED_TEXT
|
|
id unsupportedTextId = self.fileTypeSupport[UNSUPPORTED_TEXT];
|
|
if (unsupportedTextId) {
|
|
NSString *unsupportedText = [RCTConvert NSString:unsupportedTextId];
|
|
supportedDict[UNSUPPORTED_TEXT] = unsupportedText;
|
|
}
|
|
|
|
// UNSUPPORTED_TEXT_COLOR
|
|
id unsupportedTextColorId = self.fileTypeSupport[UNSUPPORTED_TEXT_COLOR];
|
|
if (unsupportedTextColorId) {
|
|
UIColor *unsupportedTextColor = [RCTConvert UIColor:unsupportedTextColorId];
|
|
supportedDict[UNSUPPORTED_TEXT_COLOR] = unsupportedTextColor;
|
|
}
|
|
|
|
[CKGalleryCollectionViewCell setSupported:supportedDict];
|
|
}
|
|
|
|
|
|
-(void)setAlbumName:(NSString *)albumName {
|
|
|
|
|
|
if ([albumName caseInsensitiveCompare:@"all photos"] == NSOrderedSame || !albumName || [albumName isEqualToString:@""]) {
|
|
|
|
PHFetchResult *allPhotosFetchResults = [PHAsset fetchAssetsWithOptions:self.fetchOptions];
|
|
[self upadateCollectionView:allPhotosFetchResults animated:(self.galleryData != nil)];
|
|
return;
|
|
}
|
|
|
|
PHFetchResult *collections = [PHCollectionList fetchTopLevelUserCollectionsWithOptions:nil];
|
|
|
|
[collections enumerateObjectsUsingBlock:^(PHAssetCollection *collection, NSUInteger idx, BOOL * _Nonnull stop) {
|
|
|
|
if ([collection.localizedTitle isEqualToString:albumName]) {
|
|
|
|
PHFetchResult *collectionFetchResults = [PHAsset fetchAssetsInAssetCollection:collection options:nil];
|
|
[self upadateCollectionView:collectionFetchResults animated:(self.galleryData != nil)];
|
|
*stop = YES;
|
|
return;
|
|
}
|
|
}];
|
|
|
|
}
|
|
|
|
|
|
#pragma mark - UICollectionViewDataSource
|
|
|
|
|
|
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
|
|
NSUInteger itemsAmount = self.galleryData.data.count;
|
|
if (self.customButtonStyle) {
|
|
itemsAmount++;
|
|
}
|
|
return itemsAmount;
|
|
}
|
|
|
|
|
|
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
|
|
|
|
NSInteger cellIndex = indexPath.row;
|
|
if (self.customButtonStyle ) {
|
|
cellIndex--;
|
|
|
|
if (indexPath.row == 0) {
|
|
CKGalleryCustomCollectionViewCell *customCell = [collectionView dequeueReusableCellWithReuseIdentifier:CustomCellReuseIdentifier forIndexPath:indexPath];
|
|
customCell.bridge = self.bridge;
|
|
[customCell applyStyle:self.customButtonStyle];
|
|
return customCell;
|
|
}
|
|
|
|
}
|
|
|
|
if ([self.galleryData.data count] < cellIndex) {
|
|
return nil;
|
|
}
|
|
|
|
NSDictionary *assetDictionary = (NSDictionary*)self.galleryData.data[cellIndex];
|
|
PHAsset *asset = assetDictionary[@"asset"];
|
|
|
|
NSString *fileType = [self extractFileTypeForAsset:asset];
|
|
|
|
CFStringRef fileExtension = (__bridge CFStringRef)[fileType pathExtension];
|
|
CFStringRef UTI = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, fileExtension, NULL);
|
|
CFStringRef MIMEType = UTTypeCopyPreferredTagWithClass(UTI, kUTTagClassMIMEType);
|
|
CFRelease(UTI);
|
|
NSString *MIMETypeString = (__bridge_transfer NSString *)MIMEType;
|
|
|
|
__block CKGalleryCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:CellReuseIdentifier forIndexPath:indexPath];
|
|
cell.delegate = self;
|
|
cell.disableSelectionIcons = self.disableSelectionIcons ? self.disableSelectionIcons.boolValue : false;
|
|
|
|
if (self.supportedFileTypesArray) {
|
|
cell.isSupported = [self.supportedFileTypesArray containsObject:[MIMETypeString lowercaseString]];
|
|
}
|
|
|
|
cell.representedAssetIdentifier = asset.localIdentifier;
|
|
|
|
[self.imageManager requestImageForAsset:asset
|
|
targetSize:CGSizeMake(self.cellSize.width*IMAGE_SIZE_MULTIPLIER, self.cellSize.height*IMAGE_SIZE_MULTIPLIER)
|
|
contentMode:PHImageContentModeDefault
|
|
options:nil
|
|
resultHandler:^(UIImage *result, NSDictionary *info) {
|
|
|
|
if ([cell.representedAssetIdentifier isEqualToString:asset.localIdentifier]) {
|
|
cell.thumbnailImage = result;
|
|
}
|
|
}];
|
|
|
|
return cell;
|
|
}
|
|
|
|
-(void)remoteDownloadingUpdate:(CKGalleryCollectionViewCell *)cell progress:(CGFloat)progress isDownloading:(BOOL)isDownloading {
|
|
if (cell.isDownloading != isDownloading) {
|
|
if (self.onRemoteDownloadChanged) {
|
|
self.onRemoteDownloadChanged(@{@"isRemoteDownloading": [NSNumber numberWithBool:isDownloading]});
|
|
}
|
|
}
|
|
cell.isDownloading = isDownloading;
|
|
cell.downloadingProgress = progress;
|
|
}
|
|
|
|
|
|
-(NSString*)extractFileTypeForAsset:(PHAsset*)asset {
|
|
return [asset valueForKey:@"uniformTypeIdentifier"];
|
|
}
|
|
|
|
-(void)downloadImageFromICloud:(PHAsset *)asset cell:(CKGalleryCollectionViewCell *)cell completion:(CompletionBlock)completion {
|
|
|
|
PHImageManager *manager = [PHImageManager defaultManager];
|
|
PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init];
|
|
options.networkAccessAllowed = YES;
|
|
options.synchronous = NO;
|
|
[options setProgressHandler:^(double progress, NSError *error, BOOL *stop, NSDictionary *info) {
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
[self remoteDownloadingUpdate:cell progress:progress isDownloading:YES];
|
|
});
|
|
}];
|
|
|
|
[manager
|
|
requestImageDataForAsset:asset
|
|
options:options
|
|
resultHandler:^(NSData * _Nullable imageData, NSString * _Nullable dataUTI, UIImageOrientation orientation, NSDictionary * _Nullable info) {
|
|
|
|
if (imageData) {
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
[self remoteDownloadingUpdate:cell progress:1 isDownloading:NO];
|
|
|
|
if (completion) {
|
|
completion(YES);
|
|
}
|
|
});
|
|
}
|
|
else {
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
[self remoteDownloadingUpdate:cell progress:0 isDownloading:NO];
|
|
if (completion) {
|
|
completion(NO);
|
|
}
|
|
});
|
|
}
|
|
|
|
}];
|
|
|
|
}
|
|
|
|
|
|
-(void)simulateICloudDownload:(NSTimer *)timer {
|
|
CKGalleryCollectionViewCell *cell = (CKGalleryCollectionViewCell *)timer.userInfo[@"cell"];
|
|
CFAbsoluteTime currentTimePassed = CFAbsoluteTimeGetCurrent() - self.timePassed;
|
|
|
|
if (currentTimePassed <= [self.iCloudDownloadSimulateTime doubleValue]) {
|
|
|
|
[self remoteDownloadingUpdate:cell
|
|
progress:currentTimePassed/[self.iCloudDownloadSimulateTime doubleValue]
|
|
isDownloading:YES];
|
|
|
|
}
|
|
else {
|
|
CompletionBlock completion = (CompletionBlock)timer.userInfo[@"completion"];
|
|
|
|
[self.iCloudDownloadSimulateTimer invalidate];
|
|
self.iCloudDownloadSimulateTimer = nil;
|
|
[self remoteDownloadingUpdate:cell
|
|
progress:1
|
|
isDownloading:NO];
|
|
|
|
if (completion) {
|
|
completion(YES);
|
|
}
|
|
}
|
|
}
|
|
|
|
-(void)downloadImageFromICloudIfNeeded:(PHAsset *)asset cell:(CKGalleryCollectionViewCell *)cell completion:(CompletionBlock)completion {
|
|
|
|
if (self.iCloudDownloadSimulateTime && !cell.isSelected) {
|
|
self.timePassed = CFAbsoluteTimeGetCurrent();
|
|
|
|
self.iCloudDownloadSimulateTimer = [NSTimer scheduledTimerWithTimeInterval:[self.iCloudDownloadSimulateTime doubleValue]/100
|
|
target:self
|
|
selector:@selector(simulateICloudDownload:)
|
|
userInfo:@{@"cell": cell, @"completion": completion}
|
|
repeats:YES];
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
PHImageManager *manager = [PHImageManager defaultManager];
|
|
PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init];
|
|
options.networkAccessAllowed = NO;
|
|
options.synchronous = YES;
|
|
|
|
[manager
|
|
requestImageDataForAsset:asset
|
|
options:options
|
|
resultHandler:^(NSData * _Nullable imageData, NSString * _Nullable dataUTI, UIImageOrientation orientation, NSDictionary * _Nullable info) {
|
|
|
|
if ([[info valueForKey:PHImageResultIsInCloudKey] boolValue]) {
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
[self remoteDownloadingUpdate:cell progress:0 isDownloading:YES];
|
|
[self downloadImageFromICloud:asset cell:cell completion:completion];
|
|
});
|
|
}
|
|
else {
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
if (completion) {
|
|
completion(YES);
|
|
}
|
|
});
|
|
}
|
|
}];
|
|
}
|
|
|
|
|
|
#pragma mark - UICollectionViewDelegate
|
|
|
|
|
|
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
|
|
|
|
NSInteger galleryDataIndex = indexPath.row;
|
|
if (self.customButtonStyle) {
|
|
galleryDataIndex--;
|
|
}
|
|
|
|
if (indexPath.row == 0 && self.onCustomButtonPress) {
|
|
self.onCustomButtonPress(@{@"selected":@"customButtonPressed"});
|
|
return;
|
|
}
|
|
|
|
id selectedCell =[collectionView cellForItemAtIndexPath:indexPath];
|
|
|
|
if ([selectedCell isKindOfClass:[CKGalleryCollectionViewCell class]]) {
|
|
CKGalleryCollectionViewCell *ckCell = (CKGalleryCollectionViewCell*)selectedCell;
|
|
|
|
NSMutableDictionary *assetDictionary = (NSMutableDictionary*)self.galleryData.data[galleryDataIndex];
|
|
PHAsset *asset = assetDictionary[@"asset"];
|
|
NSNumber *isSelectedNumber = assetDictionary[@"isSelected"];
|
|
assetDictionary[@"isSelected"] = [NSNumber numberWithBool:!(isSelectedNumber.boolValue)];
|
|
|
|
[self downloadImageFromICloudIfNeeded:asset cell:ckCell completion:^(BOOL success) {
|
|
|
|
if (success) {
|
|
if (!ckCell.isSupported) {
|
|
return;
|
|
}
|
|
|
|
ckCell.isSelected = !ckCell.isSelected;
|
|
|
|
[self onSelectChanged:asset isSelected:ckCell.isSelected];
|
|
}
|
|
}];
|
|
}
|
|
}
|
|
|
|
- (void)collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath {
|
|
if([cell isKindOfClass:[CKGalleryCollectionViewCell class]]) {
|
|
CKGalleryCollectionViewCell *ckCell = (CKGalleryCollectionViewCell*)cell;
|
|
|
|
NSInteger galleryDataIndex = indexPath.row;
|
|
if (self.customButtonStyle) {
|
|
galleryDataIndex--;
|
|
}
|
|
NSDictionary *assetDictionary = (NSDictionary*)self.galleryData.data[galleryDataIndex];
|
|
ckCell.isSelected = ((NSNumber*)assetDictionary[@"isSelected"]).boolValue;
|
|
}
|
|
}
|
|
|
|
-(BOOL)isSelectionDirty:(NSMutableArray *)newSelectedImages {
|
|
if(![self.autoSyncSelection boolValue]) {
|
|
return NO;
|
|
}
|
|
NSArray *mergedArray = [newSelectedImages arrayByAddingObjectsFromArray:self.selectedImages];
|
|
NSArray *arrayWithoutDuplicates = [[NSOrderedSet orderedSetWithArray:mergedArray] array];
|
|
return (arrayWithoutDuplicates.count > 0);
|
|
}
|
|
|
|
-(void)setSelectedImages:(NSMutableArray *)selectedImages {
|
|
BOOL selectionDirty = [self isSelectionDirty:selectedImages];
|
|
if (selectedImages) {
|
|
_selectedImages = selectedImages;
|
|
}
|
|
|
|
if(selectionDirty && [self.autoSyncSelection boolValue]) {
|
|
//sync visible cells
|
|
for (CKGalleryCollectionViewCell *cell in [self.collectionView visibleCells]) {
|
|
if([cell respondsToSelector:@selector(representedAssetIdentifier)]) {
|
|
cell.isSelected = ([selectedImages indexOfObject:cell.representedAssetIdentifier] != NSNotFound);
|
|
}
|
|
}
|
|
//sync data
|
|
for (NSMutableDictionary *dataDic in self.galleryData.data) {
|
|
PHAsset *asset = dataDic[@"asset"];
|
|
dataDic[@"isSelected"] = @([selectedImages indexOfObject:asset.localIdentifier] != NSNotFound);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-(void)refreshGalleryView:(NSArray*)selectedImages {
|
|
self.selectedImages = [NSMutableArray arrayWithArray:selectedImages];
|
|
[self setAlbumName:self.albumName];
|
|
}
|
|
|
|
|
|
#pragma mark - misc
|
|
|
|
|
|
-(void)onSelectChanged:(PHAsset*)asset isSelected:(BOOL)isSelected{
|
|
if (self.onTapImage) {
|
|
|
|
NSMutableDictionary *imageTapInfo = [@{@"width": [NSNumber numberWithUnsignedInteger:asset.pixelWidth],
|
|
@"height": [NSNumber numberWithUnsignedInteger:asset.pixelHeight]} mutableCopy];
|
|
|
|
BOOL shouldReturnUrl = self.getUrlOnTapImage ? [self.getUrlOnTapImage boolValue] : NO;
|
|
NSNumber *isSelectedNumber = [NSNumber numberWithBool:isSelected];
|
|
if (shouldReturnUrl) {
|
|
PHImageRequestOptions *imageRequestOptions = [[PHImageRequestOptions alloc] init];
|
|
imageRequestOptions.synchronous = YES;
|
|
NSDictionary *info = [CKGalleryViewManager infoForAsset:asset imageRequestOptions:imageRequestOptions imageQuality:self.imageQualityOnTap];
|
|
NSString *uriString = info[@"uri"];
|
|
|
|
if (uriString) {
|
|
[imageTapInfo addEntriesFromDictionary:@{@"selected": uriString, @"selectedId": asset.localIdentifier, @"isSelected": isSelectedNumber}];
|
|
self.onTapImage(imageTapInfo);
|
|
}
|
|
else {
|
|
self.onTapImage(@{@"Error": @"Could not get image uri"});
|
|
}
|
|
|
|
}
|
|
else {
|
|
[imageTapInfo addEntriesFromDictionary:@{@"selected":asset.localIdentifier, @"isSelected": isSelectedNumber}];
|
|
self.onTapImage(imageTapInfo);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
+(PHFetchResult*)filterFetchResults:(PHFetchResult*)fetchResults typesArray:(NSArray*)typesArray {
|
|
|
|
[fetchResults enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
|
|
NSLog(@"obj");
|
|
}];
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
#pragma mark - UIScrollViewDelegate methods
|
|
|
|
-(void)clearCollectionViewIsScrolling {
|
|
self.collectionViewIsScrolling = NO;
|
|
self.lastPressedCell = nil;
|
|
}
|
|
|
|
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
|
|
self.collectionViewIsScrolling = YES;
|
|
|
|
if (self.lastPressedCell != nil) {
|
|
[self.lastPressedCell setPressed:NO];
|
|
}
|
|
}
|
|
|
|
-(void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
|
|
if (!decelerate) {
|
|
[self clearCollectionViewIsScrolling];
|
|
}
|
|
}
|
|
|
|
-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
|
|
[self clearCollectionViewIsScrolling];
|
|
}
|
|
|
|
#pragma mark - CKGalleryCollectionViewCellDelegate methods
|
|
|
|
-(BOOL)shouldShowPressIndicator:(CKGalleryCollectionViewCell*)cell {
|
|
self.lastPressedCell = cell;
|
|
return !self.collectionViewIsScrolling;
|
|
}
|
|
|
|
@end
|
|
|
|
@interface CKGalleryViewManager ()
|
|
|
|
@property (nonatomic, strong) CKGalleryView *galleryView;
|
|
|
|
@end
|
|
|
|
|
|
@implementation CKGalleryViewManager
|
|
|
|
|
|
RCT_EXPORT_MODULE()
|
|
|
|
|
|
- (UIView *)view
|
|
{
|
|
self.galleryView = [[CKGalleryView alloc] init];
|
|
self.galleryView.bridge = self.bridge;
|
|
return self.galleryView;
|
|
}
|
|
|
|
|
|
RCT_EXPORT_VIEW_PROPERTY(albumName, NSString);
|
|
RCT_EXPORT_VIEW_PROPERTY(minimumLineSpacing, NSNumber);
|
|
RCT_EXPORT_VIEW_PROPERTY(minimumInteritemSpacing, NSNumber);
|
|
RCT_EXPORT_VIEW_PROPERTY(columnCount, NSNumber);
|
|
RCT_EXPORT_VIEW_PROPERTY(onTapImage, RCTDirectEventBlock);
|
|
RCT_EXPORT_VIEW_PROPERTY(selectedImageIcon, UIImage);
|
|
RCT_EXPORT_VIEW_PROPERTY(unSelectedImageIcon, UIImage);
|
|
RCT_EXPORT_VIEW_PROPERTY(selectedImages, NSArray);
|
|
RCT_EXPORT_VIEW_PROPERTY(fileTypeSupport, NSDictionary);
|
|
RCT_EXPORT_VIEW_PROPERTY(imageStrokeColor, UIColor);
|
|
RCT_EXPORT_VIEW_PROPERTY(imageStrokeColorWidth, NSNumber);
|
|
RCT_EXPORT_VIEW_PROPERTY(disableSelectionIcons, NSNumber);
|
|
RCT_EXPORT_VIEW_PROPERTY(customButtonStyle, NSDictionary);
|
|
RCT_EXPORT_VIEW_PROPERTY(onCustomButtonPress, RCTDirectEventBlock);
|
|
RCT_EXPORT_VIEW_PROPERTY(getUrlOnTapImage, NSNumber);
|
|
RCT_EXPORT_VIEW_PROPERTY(autoSyncSelection, NSNumber);
|
|
RCT_EXPORT_VIEW_PROPERTY(selection, NSDictionary);
|
|
RCT_EXPORT_VIEW_PROPERTY(contentInset, UIEdgeInsets);
|
|
RCT_EXPORT_VIEW_PROPERTY(imageQualityOnTap, NSString);
|
|
RCT_EXPORT_VIEW_PROPERTY(alwaysBounce, BOOL);
|
|
RCT_EXPORT_VIEW_PROPERTY(isHorizontal, BOOL);
|
|
RCT_EXPORT_VIEW_PROPERTY(remoteDownloadIndicatorColor, UIColor);
|
|
RCT_EXPORT_VIEW_PROPERTY(remoteDownloadIndicatorType, NSString);
|
|
RCT_EXPORT_VIEW_PROPERTY(onRemoteDownloadChanged, RCTDirectEventBlock);
|
|
RCT_EXPORT_VIEW_PROPERTY(iCloudDownloadSimulateTime, NSNumber);
|
|
|
|
|
|
|
|
|
|
RCT_EXPORT_METHOD(getSelectedImages:(RCTPromiseResolveBlock)resolve
|
|
reject:(RCTPromiseRejectBlock)reject) {
|
|
|
|
NSError *error = nil;
|
|
NSURL *directoryURL = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]] isDirectory:YES];
|
|
[[NSFileManager defaultManager] createDirectoryAtURL:directoryURL withIntermediateDirectories:YES attributes:nil error:&error];
|
|
|
|
NSMutableArray *assetsUrls = [[NSMutableArray alloc] init];
|
|
|
|
for (PHAsset *asset in self.galleryView.selectedImages) {
|
|
|
|
[self.galleryView.imageManager requestImageDataForAsset:asset options:nil resultHandler:^(NSData * _Nullable imageData, NSString * _Nullable dataUTI, UIImageOrientation orientation, NSDictionary * _Nullable info) {
|
|
|
|
NSURL *fileURLKey = info[@"PHImageFileURLKey"];
|
|
if (!fileURLKey) {
|
|
if (resolve) {
|
|
resolve(nil);
|
|
}
|
|
}
|
|
|
|
NSMutableDictionary *assetInfoDict = [[NSMutableDictionary alloc] init];
|
|
imageData = [CKGalleryViewManager handleNonJPEGOrPNGFormatsData:imageData dataUTI:dataUTI];
|
|
NSString *fileName = ((NSURL*)info[@"PHImageFileURLKey"]).lastPathComponent;
|
|
|
|
fileName = [CKGalleryViewManager handleNonJPEGOrPNGFormatsFileName:fileName dataUTI:dataUTI];
|
|
if (fileName) {
|
|
assetInfoDict[@"name"] = fileName;
|
|
}
|
|
|
|
float imageSize = imageData.length;
|
|
assetInfoDict[@"size"] = [NSNumber numberWithFloat:imageSize];
|
|
|
|
NSURL *fileURL = [directoryURL URLByAppendingPathComponent:fileName];
|
|
NSError *error = nil;
|
|
[imageData writeToURL:fileURL options:NSDataWritingAtomic error:&error];
|
|
|
|
if (!error && fileURL) {
|
|
assetInfoDict[@"uri"] = fileURL.absoluteString;
|
|
}
|
|
|
|
[assetsUrls addObject:assetInfoDict];
|
|
|
|
if (asset == self.galleryView.selectedImages.lastObject) {
|
|
if (resolve) {
|
|
resolve(@{@"selectedImages":assetsUrls});
|
|
}
|
|
}
|
|
}];
|
|
}
|
|
}
|
|
|
|
|
|
RCT_EXPORT_METHOD(refreshGalleryView:(NSArray*)selectedImages
|
|
resolve:(RCTPromiseResolveBlock)resolve
|
|
reject:(RCTPromiseRejectBlock)reject) {
|
|
NSMutableArray *newArray = [[NSMutableArray alloc] initWithArray:selectedImages];
|
|
[self.galleryView refreshGalleryView:newArray];
|
|
|
|
if (resolve)
|
|
resolve(@YES);
|
|
}
|
|
|
|
RCT_EXPORT_METHOD(modifyGalleryViewContentOffset:(NSDictionary*)params) {
|
|
CGPoint newOffset = self.galleryView.collectionView.contentOffset;
|
|
if(params[@"x"] != nil) {
|
|
newOffset.x += [params[@"x"] floatValue];
|
|
}
|
|
if(params[@"y"] != nil) {
|
|
newOffset.y += [params[@"y"] floatValue];
|
|
}
|
|
[self.galleryView.collectionView setContentOffset:newOffset];
|
|
}
|
|
|
|
#pragma mark - Static functions
|
|
|
|
|
|
+(NSMutableDictionary*)infoForAsset:(PHAsset*)asset
|
|
imageRequestOptions:(PHImageRequestOptions*)imageRequestOptions
|
|
imageQuality:(NSString*)imageQuality {
|
|
|
|
NSError *error = nil;
|
|
NSURL *directoryURL = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]] isDirectory:YES];
|
|
|
|
[[NSFileManager defaultManager] createDirectoryAtURL:directoryURL withIntermediateDirectories:YES attributes:nil error:&error];
|
|
|
|
if (error) {
|
|
//NSLog(@"ERROR while creating directory:%@",error);
|
|
}
|
|
|
|
|
|
__block NSMutableDictionary *assetInfoDict = nil;
|
|
|
|
[[PHCachingImageManager defaultManager] requestImageDataForAsset:asset options:imageRequestOptions resultHandler:^(NSData * _Nullable imageData, NSString * _Nullable dataUTI, UIImageOrientation orientation, NSDictionary * _Nullable info) {
|
|
|
|
NSString *fileName = ((NSURL*)info[@"PHImageFileURLKey"]).lastPathComponent;
|
|
fileName = [CKGalleryViewManager handleNonJPEGOrPNGFormatsFileName:fileName dataUTI:dataUTI];
|
|
imageData = [CKGalleryViewManager handleNonJPEGOrPNGFormatsData:imageData dataUTI:dataUTI];
|
|
|
|
NSData *compressedImageData = imageData;
|
|
|
|
UIImage *compressedImage = [UIImage imageWithData:imageData];
|
|
|
|
NSURL *fileURLKey = info[@"PHImageFileURLKey"];
|
|
|
|
if (fileURLKey) {
|
|
|
|
assetInfoDict = [[NSMutableDictionary alloc] init];
|
|
|
|
assetInfoDict[@"width"] = [NSNumber numberWithFloat:compressedImage.size.width];
|
|
assetInfoDict[@"height"] = [NSNumber numberWithFloat:compressedImage.size.height];
|
|
|
|
if (fileName) {
|
|
assetInfoDict[@"name"] = fileName;
|
|
} else {
|
|
fileName = @"";
|
|
}
|
|
|
|
float imageSize = 0;
|
|
if (compressedImageData) {
|
|
imageSize = compressedImageData.length;
|
|
}
|
|
assetInfoDict[@"size"] = [NSNumber numberWithFloat:imageSize];
|
|
|
|
NSURL *fileURL = [directoryURL URLByAppendingPathComponent:fileName];
|
|
NSError *error = nil;
|
|
|
|
[compressedImageData writeToURL:fileURL options:NSDataWritingAtomic error:&error];
|
|
|
|
if (!error && fileURL) {
|
|
assetInfoDict[@"uri"] = fileURL.absoluteString;
|
|
}
|
|
else if (error){
|
|
//NSLog(@"%@", error);
|
|
}
|
|
}
|
|
}];
|
|
|
|
if (assetInfoDict && asset) {
|
|
assetInfoDict[@"asset"] = asset;
|
|
}
|
|
|
|
return assetInfoDict;
|
|
}
|
|
|
|
|
|
+(NSData*)handleNonJPEGOrPNGFormatsData:(NSData*)imageData dataUTI:(NSString*)dataUTI {
|
|
NSData *ans = imageData;
|
|
if([dataUTI isEqualToString:(__bridge NSString*)kUTTypeJPEG] == NO && [dataUTI isEqualToString:(__bridge NSString*)kUTTypePNG] == NO)
|
|
{
|
|
CIImage* image = [CIImage imageWithData:imageData];
|
|
CIContext* context = [CIContext contextWithOptions:nil];
|
|
|
|
if ([context respondsToSelector:@selector(JPEGRepresentationOfImage:colorSpace:options:)]) {
|
|
ans = [context JPEGRepresentationOfImage:image
|
|
colorSpace:CGColorSpaceCreateWithName(kCGColorSpaceSRGB)
|
|
options:@{(NSString*)kCGImageDestinationLossyCompressionQuality: @1.0}];
|
|
}
|
|
}
|
|
return ans;
|
|
}
|
|
|
|
+(NSString*)handleNonJPEGOrPNGFormatsFileName:(NSString*)fileName dataUTI:(NSString*)dataUTI {
|
|
NSString *ans = fileName;
|
|
if([dataUTI isEqualToString:(__bridge NSString*)kUTTypeJPEG] == NO && [dataUTI isEqualToString:(__bridge NSString*)kUTTypePNG] == NO)
|
|
{
|
|
ans = [[fileName lastPathComponent] stringByDeletingPathExtension];
|
|
ans = [fileName stringByAppendingPathExtension:@"JPG"];
|
|
}
|
|
return ans;
|
|
}
|
|
|
|
|
|
@end
|