// // TZPhotoPickerController.m // TZImagePickerController // // Created by 谭真 on 15/12/24. // Copyright © 2015年 谭真. All rights reserved. // #import "TZPhotoPickerController.h" #import "TZImagePickerController.h" #import "TZPhotoPreviewController.h" #import "TZAssetCell.h" #import "TZAssetModel.h" #import "UIView+Layout.h" #import "TZImageManager.h" #import "TZVideoPlayerController.h" #import "TZGifPhotoPreviewController.h" @interface TZPhotoPickerController () { NSMutableArray *_models; UIButton *_previewButton; UIButton *_doneButton; UIImageView *_numberImageView; UILabel *_numberLabel; UIButton *_originalPhotoButton; UILabel *_originalPhotoLabel; BOOL _shouldScrollToBottom; BOOL _showTakePhotoBtn; } @property CGRect previousPreheatRect; @property (nonatomic, assign) BOOL isSelectOriginalPhoto; @property (nonatomic, strong) TZCollectionView *collectionView; @property (nonatomic, strong) UIImagePickerController *imagePickerVc; @end static CGSize AssetGridThumbnailSize; @implementation TZPhotoPickerController #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" - (UIImagePickerController *)imagePickerVc { if (_imagePickerVc == nil) { _imagePickerVc = [[UIImagePickerController alloc] init]; _imagePickerVc.delegate = self; // set appearance / 改变相册选择页的导航栏外观 _imagePickerVc.navigationBar.barTintColor = self.navigationController.navigationBar.barTintColor; _imagePickerVc.navigationBar.tintColor = self.navigationController.navigationBar.tintColor; UIBarButtonItem *tzBarItem, *BarItem; if (iOS9Later) { tzBarItem = [UIBarButtonItem appearanceWhenContainedInInstancesOfClasses:@[[TZImagePickerController class]]]; BarItem = [UIBarButtonItem appearanceWhenContainedInInstancesOfClasses:@[[UIImagePickerController class]]]; } else { tzBarItem = [UIBarButtonItem appearanceWhenContainedIn:[TZImagePickerController class], nil]; BarItem = [UIBarButtonItem appearanceWhenContainedIn:[UIImagePickerController class], nil]; } NSDictionary *titleTextAttributes = [tzBarItem titleTextAttributesForState:UIControlStateNormal]; [BarItem setTitleTextAttributes:titleTextAttributes forState:UIControlStateNormal]; } return _imagePickerVc; } - (void)viewDidLoad { [super viewDidLoad]; TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController; _isSelectOriginalPhoto = tzImagePickerVc.isSelectOriginalPhoto; _shouldScrollToBottom = YES; self.view.backgroundColor = [UIColor whiteColor]; self.navigationItem.title = _model.name; self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:tzImagePickerVc.cancelBtnTitleStr style:UIBarButtonItemStylePlain target:tzImagePickerVc action:@selector(cancelButtonClick)]; _showTakePhotoBtn = (([[TZImageManager manager] isCameraRollAlbum:_model.name]) && tzImagePickerVc.allowTakePicture); if (!tzImagePickerVc.sortAscendingByModificationDate && _isFirstAppear && iOS8Later) { [[TZImageManager manager] getCameraRollAlbum:tzImagePickerVc.allowPickingVideo allowPickingImage:tzImagePickerVc.allowPickingImage completion:^(TZAlbumModel *model) { _model = model; _models = [NSMutableArray arrayWithArray:_model.models]; [self initSubviews]; }]; } else { if (_showTakePhotoBtn || !iOS8Later || _isFirstAppear) { [[TZImageManager manager] getAssetsFromFetchResult:_model.result allowPickingVideo:tzImagePickerVc.allowPickingVideo allowPickingImage:tzImagePickerVc.allowPickingImage completion:^(NSArray *models) { _models = [NSMutableArray arrayWithArray:models]; [self initSubviews]; }]; } else { _models = [NSMutableArray arrayWithArray:_model.models]; [self initSubviews]; } } // [self resetCachedAssets]; } - (void)initSubviews { [self checkSelectedModels]; [self configCollectionView]; [self configBottomToolBar]; } - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController; tzImagePickerVc.isSelectOriginalPhoto = _isSelectOriginalPhoto; if (self.backButtonClickHandle) { self.backButtonClickHandle(_model); } } - (BOOL)prefersStatusBarHidden { return NO; } - (void)configCollectionView { TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController; UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; CGFloat margin = 5; CGFloat itemWH = (self.view.tz_width - (self.columnNumber + 1) * margin) / self.columnNumber; layout.itemSize = CGSizeMake(itemWH, itemWH); layout.minimumInteritemSpacing = margin; layout.minimumLineSpacing = margin; CGFloat top = 0; CGFloat collectionViewHeight = 0; if (self.navigationController.navigationBar.isTranslucent) { top = 44; if (iOS7Later) top += 20; collectionViewHeight = tzImagePickerVc.showSelectBtn ? self.view.tz_height - 50 - top : self.view.tz_height - top;; } else { CGFloat navigationHeight = 44; if (iOS7Later) navigationHeight += 20; collectionViewHeight = tzImagePickerVc.showSelectBtn ? self.view.tz_height - 50 - navigationHeight : self.view.tz_height - navigationHeight; } _collectionView = [[TZCollectionView alloc] initWithFrame:CGRectMake(0, top, self.view.tz_width, collectionViewHeight) collectionViewLayout:layout]; _collectionView.backgroundColor = [UIColor whiteColor]; _collectionView.dataSource = self; _collectionView.delegate = self; _collectionView.alwaysBounceHorizontal = NO; _collectionView.contentInset = UIEdgeInsetsMake(margin, margin, margin, margin); if (_showTakePhotoBtn && tzImagePickerVc.allowTakePicture ) { _collectionView.contentSize = CGSizeMake(self.view.tz_width, ((_model.count + self.columnNumber) / self.columnNumber) * self.view.tz_width); } else { _collectionView.contentSize = CGSizeMake(self.view.tz_width, ((_model.count + self.columnNumber - 1) / self.columnNumber) * self.view.tz_width); } [self.view addSubview:_collectionView]; [_collectionView registerClass:[TZAssetCell class] forCellWithReuseIdentifier:@"TZAssetCell"]; [_collectionView registerClass:[TZAssetCameraCell class] forCellWithReuseIdentifier:@"TZAssetCameraCell"]; } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; [self scrollCollectionViewToBottom]; // Determine the size of the thumbnails to request from the PHCachingImageManager CGFloat scale = 2.0; if ([UIScreen mainScreen].bounds.size.width > 600) { scale = 1.0; } CGSize cellSize = ((UICollectionViewFlowLayout *)_collectionView.collectionViewLayout).itemSize; AssetGridThumbnailSize = CGSizeMake(cellSize.width * scale, cellSize.height * scale); } - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; if (iOS8Later) { // [self updateCachedAssets]; } } - (void)configBottomToolBar { TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController; if (!tzImagePickerVc.showSelectBtn) return; CGFloat yOffset = 0; if (self.navigationController.navigationBar.isTranslucent) { yOffset = self.view.tz_height - 50; } else { CGFloat navigationHeight = 44; if (iOS7Later) navigationHeight += 20; yOffset = self.view.tz_height - 50 - navigationHeight; } UIView *bottomToolBar = [[UIView alloc] initWithFrame:CGRectMake(0, yOffset, self.view.tz_width, 50)]; CGFloat rgb = 253 / 255.0; bottomToolBar.backgroundColor = [UIColor colorWithRed:rgb green:rgb blue:rgb alpha:1.0]; CGFloat previewWidth = [tzImagePickerVc.previewBtnTitleStr boundingRectWithSize:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX) options:NSStringDrawingUsesFontLeading attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:16]} context:nil].size.width + 2; if (!tzImagePickerVc.allowPreview) { previewWidth = 0.0; } _previewButton = [UIButton buttonWithType:UIButtonTypeCustom]; _previewButton.frame = CGRectMake(10, 3, previewWidth, 44); _previewButton.tz_width = !tzImagePickerVc.showSelectBtn ? 0 : previewWidth; [_previewButton addTarget:self action:@selector(previewButtonClick) forControlEvents:UIControlEventTouchUpInside]; _previewButton.titleLabel.font = [UIFont systemFontOfSize:16]; [_previewButton setTitle:tzImagePickerVc.previewBtnTitleStr forState:UIControlStateNormal]; [_previewButton setTitle:tzImagePickerVc.previewBtnTitleStr forState:UIControlStateDisabled]; [_previewButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; [_previewButton setTitleColor:[UIColor lightGrayColor] forState:UIControlStateDisabled]; _previewButton.enabled = tzImagePickerVc.selectedModels.count; if (tzImagePickerVc.allowPickingOriginalPhoto) { CGFloat fullImageWidth = [tzImagePickerVc.fullImageBtnTitleStr boundingRectWithSize:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX) options:NSStringDrawingUsesFontLeading attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:13]} context:nil].size.width; _originalPhotoButton = [UIButton buttonWithType:UIButtonTypeCustom]; _originalPhotoButton.frame = CGRectMake(CGRectGetMaxX(_previewButton.frame), self.view.tz_height - 50, fullImageWidth + 56, 50); _originalPhotoButton.imageEdgeInsets = UIEdgeInsetsMake(0, -10, 0, 0); [_originalPhotoButton addTarget:self action:@selector(originalPhotoButtonClick) forControlEvents:UIControlEventTouchUpInside]; _originalPhotoButton.titleLabel.font = [UIFont systemFontOfSize:16]; [_originalPhotoButton setTitle:tzImagePickerVc.fullImageBtnTitleStr forState:UIControlStateNormal]; [_originalPhotoButton setTitle:tzImagePickerVc.fullImageBtnTitleStr forState:UIControlStateSelected]; [_originalPhotoButton setTitleColor:[UIColor lightGrayColor] forState:UIControlStateNormal]; [_originalPhotoButton setTitleColor:[UIColor blackColor] forState:UIControlStateSelected]; [_originalPhotoButton setImage:[UIImage imageNamedFromMyBundle:tzImagePickerVc.photoOriginDefImageName] forState:UIControlStateNormal]; [_originalPhotoButton setImage:[UIImage imageNamedFromMyBundle:tzImagePickerVc.photoOriginSelImageName] forState:UIControlStateSelected]; _originalPhotoButton.selected = _isSelectOriginalPhoto; _originalPhotoButton.enabled = tzImagePickerVc.selectedModels.count > 0; _originalPhotoLabel = [[UILabel alloc] init]; _originalPhotoLabel.frame = CGRectMake(fullImageWidth + 46, 0, 80, 50); _originalPhotoLabel.textAlignment = NSTextAlignmentLeft; _originalPhotoLabel.font = [UIFont systemFontOfSize:16]; _originalPhotoLabel.textColor = [UIColor blackColor]; if (_isSelectOriginalPhoto) [self getSelectedPhotoBytes]; } _doneButton = [UIButton buttonWithType:UIButtonTypeCustom]; _doneButton.frame = CGRectMake(self.view.tz_width - 44 - 12, 3, 44, 44); _doneButton.titleLabel.font = [UIFont systemFontOfSize:16]; [_doneButton addTarget:self action:@selector(doneButtonClick) forControlEvents:UIControlEventTouchUpInside]; [_doneButton setTitle:tzImagePickerVc.doneBtnTitleStr forState:UIControlStateNormal]; [_doneButton setTitle:tzImagePickerVc.doneBtnTitleStr forState:UIControlStateDisabled]; [_doneButton setTitleColor:tzImagePickerVc.oKButtonTitleColorNormal forState:UIControlStateNormal]; [_doneButton setTitleColor:tzImagePickerVc.oKButtonTitleColorDisabled forState:UIControlStateDisabled]; _doneButton.enabled = tzImagePickerVc.selectedModels.count || tzImagePickerVc.alwaysEnableDoneBtn; _numberImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamedFromMyBundle:tzImagePickerVc.photoNumberIconImageName]]; _numberImageView.frame = CGRectMake(self.view.tz_width - 56 - 28, 10, 30, 30); _numberImageView.hidden = tzImagePickerVc.selectedModels.count <= 0; _numberImageView.backgroundColor = [UIColor clearColor]; _numberLabel = [[UILabel alloc] init]; _numberLabel.frame = _numberImageView.frame; _numberLabel.font = [UIFont systemFontOfSize:15]; _numberLabel.textColor = [UIColor whiteColor]; _numberLabel.textAlignment = NSTextAlignmentCenter; _numberLabel.text = [NSString stringWithFormat:@"%zd",tzImagePickerVc.selectedModels.count]; _numberLabel.hidden = tzImagePickerVc.selectedModels.count <= 0; _numberLabel.backgroundColor = [UIColor clearColor]; UIView *divide = [[UIView alloc] init]; CGFloat rgb2 = 222 / 255.0; divide.backgroundColor = [UIColor colorWithRed:rgb2 green:rgb2 blue:rgb2 alpha:1.0]; divide.frame = CGRectMake(0, 0, self.view.tz_width, 1); [bottomToolBar addSubview:divide]; [bottomToolBar addSubview:_previewButton]; [bottomToolBar addSubview:_doneButton]; [bottomToolBar addSubview:_numberImageView]; [bottomToolBar addSubview:_numberLabel]; [self.view addSubview:bottomToolBar]; [self.view addSubview:_originalPhotoButton]; [_originalPhotoButton addSubview:_originalPhotoLabel]; } #pragma mark - Click Event - (void)previewButtonClick { TZPhotoPreviewController *photoPreviewVc = [[TZPhotoPreviewController alloc] init]; [self pushPhotoPrevireViewController:photoPreviewVc]; } - (void)originalPhotoButtonClick { _originalPhotoButton.selected = !_originalPhotoButton.isSelected; _isSelectOriginalPhoto = _originalPhotoButton.isSelected; _originalPhotoLabel.hidden = !_originalPhotoButton.isSelected; if (_isSelectOriginalPhoto) [self getSelectedPhotoBytes]; } - (void)doneButtonClick { TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController; // 1.6.8 判断是否满足最小必选张数的限制 if (tzImagePickerVc.minImagesCount && tzImagePickerVc.selectedModels.count < tzImagePickerVc.minImagesCount) { NSString *title = [NSString stringWithFormat:[NSBundle tz_localizedStringForKey:@"Select a minimum of %zd photos"], tzImagePickerVc.minImagesCount]; [tzImagePickerVc showAlertWithTitle:title]; return; } [tzImagePickerVc showProgressHUD]; NSMutableArray *photos = [NSMutableArray array]; NSMutableArray *assets = [NSMutableArray array]; NSMutableArray *infoArr = [NSMutableArray array]; for (NSInteger i = 0; i < tzImagePickerVc.selectedModels.count; i++) { [photos addObject:@1];[assets addObject:@1];[infoArr addObject:@1]; } __block BOOL havenotShowAlert = YES; [TZImageManager manager].shouldFixOrientation = YES; for (NSInteger i = 0; i < tzImagePickerVc.selectedModels.count; i++) { TZAssetModel *model = tzImagePickerVc.selectedModels[i]; [[TZImageManager manager] getPhotoWithAsset:model.asset completion:^(UIImage *photo, NSDictionary *info, BOOL isDegraded) { if (isDegraded) return; if (photo) { photo = [self scaleImage:photo toSize:CGSizeMake(tzImagePickerVc.photoWidth, (int)(tzImagePickerVc.photoWidth * photo.size.height / photo.size.width))]; [photos replaceObjectAtIndex:i withObject:photo]; } if (info) [infoArr replaceObjectAtIndex:i withObject:info]; [assets replaceObjectAtIndex:i withObject:model.asset]; for (id item in photos) { if ([item isKindOfClass:[NSNumber class]]) return; } if (havenotShowAlert) { [self didGetAllPhotos:photos assets:assets infoArr:infoArr]; } } progressHandler:^(double progress, NSError *error, BOOL *stop, NSDictionary *info) { // 如果图片正在从iCloud同步中,提醒用户 if (progress < 1 && havenotShowAlert) { [tzImagePickerVc hideProgressHUD]; [tzImagePickerVc showAlertWithTitle:[NSBundle tz_localizedStringForKey:@"Synchronizing photos from iCloud"]]; havenotShowAlert = NO; return; } } networkAccessAllowed:YES]; } if (tzImagePickerVc.selectedModels.count <= 0) { [self didGetAllPhotos:photos assets:assets infoArr:infoArr]; } } - (void)didGetAllPhotos:(NSArray *)photos assets:(NSArray *)assets infoArr:(NSArray *)infoArr { TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController; [tzImagePickerVc hideProgressHUD]; if (tzImagePickerVc.autoDismiss) { [self.navigationController dismissViewControllerAnimated:YES completion:^{ [self callDelegateMethodWithPhotos:photos assets:assets infoArr:infoArr]; }]; } else { [self callDelegateMethodWithPhotos:photos assets:assets infoArr:infoArr]; } } - (void)callDelegateMethodWithPhotos:(NSArray *)photos assets:(NSArray *)assets infoArr:(NSArray *)infoArr { TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController; if ([tzImagePickerVc.pickerDelegate respondsToSelector:@selector(imagePickerController:didFinishPickingPhotos:sourceAssets:isSelectOriginalPhoto:)]) { [tzImagePickerVc.pickerDelegate imagePickerController:tzImagePickerVc didFinishPickingPhotos:photos sourceAssets:assets isSelectOriginalPhoto:_isSelectOriginalPhoto]; } if ([tzImagePickerVc.pickerDelegate respondsToSelector:@selector(imagePickerController:didFinishPickingPhotos:sourceAssets:isSelectOriginalPhoto:infos:)]) { [tzImagePickerVc.pickerDelegate imagePickerController:tzImagePickerVc didFinishPickingPhotos:photos sourceAssets:assets isSelectOriginalPhoto:_isSelectOriginalPhoto infos:infoArr]; } if (tzImagePickerVc.didFinishPickingPhotosHandle) { tzImagePickerVc.didFinishPickingPhotosHandle(photos,assets,_isSelectOriginalPhoto); } if (tzImagePickerVc.didFinishPickingPhotosWithInfosHandle) { tzImagePickerVc.didFinishPickingPhotosWithInfosHandle(photos,assets,_isSelectOriginalPhoto,infoArr); } } #pragma mark - UICollectionViewDataSource && Delegate - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { if (_showTakePhotoBtn) { TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController; if (tzImagePickerVc.allowPickingImage && tzImagePickerVc.allowTakePicture) { return _models.count + 1; } } return _models.count; } - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { // the cell lead to take a picture / 去拍照的cell TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController; if (((tzImagePickerVc.sortAscendingByModificationDate && indexPath.row >= _models.count) || (!tzImagePickerVc.sortAscendingByModificationDate && indexPath.row == 0)) && _showTakePhotoBtn) { TZAssetCameraCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"TZAssetCameraCell" forIndexPath:indexPath]; cell.imageView.image = [UIImage imageNamedFromMyBundle:tzImagePickerVc.takePictureImageName]; return cell; } // the cell dipaly photo or video / 展示照片或视频的cell TZAssetCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"TZAssetCell" forIndexPath:indexPath]; cell.photoDefImageName = tzImagePickerVc.photoDefImageName; cell.photoSelImageName = tzImagePickerVc.photoSelImageName; TZAssetModel *model; if (tzImagePickerVc.sortAscendingByModificationDate || !_showTakePhotoBtn) { model = _models[indexPath.row]; } else { model = _models[indexPath.row - 1]; } cell.allowPickingGif = tzImagePickerVc.allowPickingGif; cell.model = model; cell.showSelectBtn = tzImagePickerVc.showSelectBtn; if (!tzImagePickerVc.allowPreview) { cell.selectPhotoButton.frame = cell.bounds; } __weak typeof(cell) weakCell = cell; __weak typeof(self) weakSelf = self; __weak typeof(_numberImageView.layer) weakLayer = _numberImageView.layer; cell.didSelectPhotoBlock = ^(BOOL isSelected) { TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)weakSelf.navigationController; // 1. cancel select / 取消选择 if (isSelected) { weakCell.selectPhotoButton.selected = NO; model.isSelected = NO; NSArray *selectedModels = [NSArray arrayWithArray:tzImagePickerVc.selectedModels]; for (TZAssetModel *model_item in selectedModels) { if ([[[TZImageManager manager] getAssetIdentifier:model.asset] isEqualToString:[[TZImageManager manager] getAssetIdentifier:model_item.asset]]) { [tzImagePickerVc.selectedModels removeObject:model_item]; break; } } [weakSelf refreshBottomToolBarStatus]; } else { // 2. select:check if over the maxImagesCount / 选择照片,检查是否超过了最大个数的限制 if (tzImagePickerVc.selectedModels.count < tzImagePickerVc.maxImagesCount) { weakCell.selectPhotoButton.selected = YES; model.isSelected = YES; [tzImagePickerVc.selectedModels addObject:model]; [weakSelf refreshBottomToolBarStatus]; } else { NSString *title = [NSString stringWithFormat:[NSBundle tz_localizedStringForKey:@"Select a maximum of %zd photos"], tzImagePickerVc.maxImagesCount]; [tzImagePickerVc showAlertWithTitle:title]; } } [UIView showOscillatoryAnimationWithLayer:weakLayer type:TZOscillatoryAnimationToSmaller]; }; return cell; } - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath { // take a photo / 去拍照 TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController; if (((tzImagePickerVc.sortAscendingByModificationDate && indexPath.row >= _models.count) || (!tzImagePickerVc.sortAscendingByModificationDate && indexPath.row == 0)) && _showTakePhotoBtn) { [self takePhoto]; return; } // preview phote or video / 预览照片或视频 NSInteger index = indexPath.row; if (!tzImagePickerVc.sortAscendingByModificationDate && _showTakePhotoBtn) { index = indexPath.row - 1; } TZAssetModel *model = _models[index]; if (model.type == TZAssetModelMediaTypeVideo) { if (tzImagePickerVc.selectedModels.count > 0) { TZImagePickerController *imagePickerVc = (TZImagePickerController *)self.navigationController; [imagePickerVc showAlertWithTitle:[NSBundle tz_localizedStringForKey:@"Can not choose both video and photo"]]; } else { TZVideoPlayerController *videoPlayerVc = [[TZVideoPlayerController alloc] init]; videoPlayerVc.model = model; [self.navigationController pushViewController:videoPlayerVc animated:YES]; } } else if (model.type == TZAssetModelMediaTypePhotoGif && tzImagePickerVc.allowPickingGif) { if (tzImagePickerVc.selectedModels.count > 0) { TZImagePickerController *imagePickerVc = (TZImagePickerController *)self.navigationController; [imagePickerVc showAlertWithTitle:[NSBundle tz_localizedStringForKey:@"Can not choose both photo and GIF"]]; } else { TZGifPhotoPreviewController *gifPreviewVc = [[TZGifPhotoPreviewController alloc] init]; gifPreviewVc.model = model; [self.navigationController pushViewController:gifPreviewVc animated:YES]; } } else { TZPhotoPreviewController *photoPreviewVc = [[TZPhotoPreviewController alloc] init]; photoPreviewVc.currentIndex = index; photoPreviewVc.models = _models; [self pushPhotoPrevireViewController:photoPreviewVc]; } } #pragma mark - UIScrollViewDelegate - (void)scrollViewDidScroll:(UIScrollView *)scrollView { if (iOS8Later) { // [self updateCachedAssets]; } } #pragma mark - Private Method - (void)takePhoto { AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo]; if ((authStatus == AVAuthorizationStatusRestricted || authStatus ==AVAuthorizationStatusDenied) && iOS7Later) { // 无权限 做一个友好的提示 NSString *appName = [[NSBundle mainBundle].infoDictionary valueForKey:@"CFBundleDisplayName"]; if (!appName) appName = [[NSBundle mainBundle].infoDictionary valueForKey:@"CFBundleName"]; NSString *message = [NSString stringWithFormat:[NSBundle tz_localizedStringForKey:@"Please allow %@ to access your camera in \"Settings -> Privacy -> Camera\""],appName]; UIAlertView * alert = [[UIAlertView alloc]initWithTitle:[NSBundle tz_localizedStringForKey:@"Can not use camera"] message:message delegate:self cancelButtonTitle:[NSBundle tz_localizedStringForKey:@"Cancel"] otherButtonTitles:[NSBundle tz_localizedStringForKey:@"Setting"], nil]; [alert show]; } else { // 调用相机 UIImagePickerControllerSourceType sourceType = UIImagePickerControllerSourceTypeCamera; if ([UIImagePickerController isSourceTypeAvailable: UIImagePickerControllerSourceTypeCamera]) { self.imagePickerVc.sourceType = sourceType; if(iOS8Later) { _imagePickerVc.modalPresentationStyle = UIModalPresentationOverCurrentContext; } [self presentViewController:_imagePickerVc animated:YES completion:nil]; } else { NSLog(@"模拟器中无法打开照相机,请在真机中使用"); } } } - (void)refreshBottomToolBarStatus { TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController; _previewButton.enabled = tzImagePickerVc.selectedModels.count > 0; _doneButton.enabled = tzImagePickerVc.selectedModels.count > 0 || tzImagePickerVc.alwaysEnableDoneBtn; _numberImageView.hidden = tzImagePickerVc.selectedModels.count <= 0; _numberLabel.hidden = tzImagePickerVc.selectedModels.count <= 0; _numberLabel.text = [NSString stringWithFormat:@"%zd",tzImagePickerVc.selectedModels.count]; _originalPhotoButton.enabled = tzImagePickerVc.selectedModels.count > 0; _originalPhotoButton.selected = (_isSelectOriginalPhoto && _originalPhotoButton.enabled); _originalPhotoLabel.hidden = (!_originalPhotoButton.isSelected); if (_isSelectOriginalPhoto) [self getSelectedPhotoBytes]; } - (void)pushPhotoPrevireViewController:(TZPhotoPreviewController *)photoPreviewVc { __weak typeof(self) weakSelf = self; photoPreviewVc.isSelectOriginalPhoto = _isSelectOriginalPhoto; [photoPreviewVc setBackButtonClickBlock:^(BOOL isSelectOriginalPhoto) { weakSelf.isSelectOriginalPhoto = isSelectOriginalPhoto; [weakSelf.collectionView reloadData]; [weakSelf refreshBottomToolBarStatus]; }]; [photoPreviewVc setDoneButtonClickBlock:^(BOOL isSelectOriginalPhoto) { weakSelf.isSelectOriginalPhoto = isSelectOriginalPhoto; [weakSelf doneButtonClick]; }]; [photoPreviewVc setDoneButtonClickBlockCropMode:^(UIImage *cropedImage, id asset) { [weakSelf didGetAllPhotos:@[cropedImage] assets:@[asset] infoArr:nil]; }]; [self.navigationController pushViewController:photoPreviewVc animated:YES]; } - (void)getSelectedPhotoBytes { TZImagePickerController *imagePickerVc = (TZImagePickerController *)self.navigationController; [[TZImageManager manager] getPhotosBytesWithArray:imagePickerVc.selectedModels completion:^(NSString *totalBytes) { _originalPhotoLabel.text = [NSString stringWithFormat:@"(%@)",totalBytes]; }]; } /// Scale image / 缩放图片 - (UIImage *)scaleImage:(UIImage *)image toSize:(CGSize)size { if (image.size.width < size.width) { return image; } UIGraphicsBeginImageContext(size); [image drawInRect:CGRectMake(0, 0, size.width, size.height)]; UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return newImage; } - (void)scrollCollectionViewToBottom { TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController; if (_shouldScrollToBottom && _models.count > 0 && tzImagePickerVc.sortAscendingByModificationDate) { NSInteger item = _models.count - 1; if (_showTakePhotoBtn) { TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController; if (tzImagePickerVc.allowPickingImage && tzImagePickerVc.allowTakePicture) { item += 1; } } [_collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:item inSection:0] atScrollPosition:UICollectionViewScrollPositionBottom animated:NO]; _shouldScrollToBottom = NO; } } - (void)checkSelectedModels { for (TZAssetModel *model in _models) { model.isSelected = NO; NSMutableArray *selectedAssets = [NSMutableArray array]; TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController; for (TZAssetModel *model in tzImagePickerVc.selectedModels) { [selectedAssets addObject:model.asset]; } if ([[TZImageManager manager] isAssetsArray:selectedAssets containAsset:model.asset]) { model.isSelected = YES; } } } #pragma mark - UIAlertViewDelegate - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex { if (buttonIndex == 1) { // 去设置界面,开启相机访问权限 if (iOS8Later) { [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]]; } else { NSURL *privacyUrl = [NSURL URLWithString:@"prefs:root=Privacy&path=CAMERA"]; if ([[UIApplication sharedApplication] canOpenURL:privacyUrl]) { [[UIApplication sharedApplication] openURL:privacyUrl]; } else { NSString *message = [NSBundle tz_localizedStringForKey:@"Can not jump to the privacy settings page, please go to the settings page by self, thank you"]; UIAlertView * alert = [[UIAlertView alloc]initWithTitle:[NSBundle tz_localizedStringForKey:@"Sorry"] message:message delegate:nil cancelButtonTitle:[NSBundle tz_localizedStringForKey:@"OK"] otherButtonTitles: nil]; [alert show]; } } } } #pragma mark - UIImagePickerControllerDelegate - (void)imagePickerController:(UIImagePickerController*)picker didFinishPickingMediaWithInfo:(NSDictionary *)info { [picker dismissViewControllerAnimated:YES completion:nil]; NSString *type = [info objectForKey:UIImagePickerControllerMediaType]; if ([type isEqualToString:@"public.image"]) { TZImagePickerController *imagePickerVc = (TZImagePickerController *)self.navigationController; [imagePickerVc showProgressHUD]; UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage]; if (image) { [[TZImageManager manager] savePhotoWithImage:image completion:^(NSError *error){ if (!error) { [self reloadPhotoArray]; } }]; } } } - (void)reloadPhotoArray { TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController; [[TZImageManager manager] getCameraRollAlbum:tzImagePickerVc.allowPickingVideo allowPickingImage:tzImagePickerVc.allowPickingImage completion:^(TZAlbumModel *model) { _model = model; [[TZImageManager manager] getAssetsFromFetchResult:_model.result allowPickingVideo:tzImagePickerVc.allowPickingVideo allowPickingImage:tzImagePickerVc.allowPickingImage completion:^(NSArray *models) { [tzImagePickerVc hideProgressHUD]; TZAssetModel *assetModel; if (tzImagePickerVc.sortAscendingByModificationDate) { assetModel = [models lastObject]; [_models addObject:assetModel]; } else { assetModel = [models firstObject]; [_models insertObject:assetModel atIndex:0]; } if (tzImagePickerVc.maxImagesCount <= 1) { if (tzImagePickerVc.allowCrop) { TZPhotoPreviewController *photoPreviewVc = [[TZPhotoPreviewController alloc] init]; photoPreviewVc.currentIndex = _models.count - 1; photoPreviewVc.models = _models; [self pushPhotoPrevireViewController:photoPreviewVc]; } else { [tzImagePickerVc.selectedModels addObject:assetModel]; [self doneButtonClick]; } return; } if (tzImagePickerVc.selectedModels.count < tzImagePickerVc.maxImagesCount) { assetModel.isSelected = YES; [tzImagePickerVc.selectedModels addObject:assetModel]; [self refreshBottomToolBarStatus]; } [_collectionView reloadData]; _shouldScrollToBottom = YES; [self scrollCollectionViewToBottom]; }]; }]; } - (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker { [picker dismissViewControllerAnimated:YES completion:nil]; } - (void)dealloc { //NSLog(@"TZPhotoPickerController dealloc"); } #pragma mark - Asset Caching - (void)resetCachedAssets { [[TZImageManager manager].cachingImageManager 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 = _collectionView.bounds; preheatRect = CGRectInset(preheatRect, 0.0f, -0.5f * CGRectGetHeight(preheatRect)); /* Check if the collection view is showing an area that is significantly different to the last preheated area. */ CGFloat delta = ABS(CGRectGetMidY(preheatRect) - CGRectGetMidY(self.previousPreheatRect)); if (delta > CGRectGetHeight(_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 aapl_indexPathsForElementsInRect:removedRect]; [removedIndexPaths addObjectsFromArray:indexPaths]; } addedHandler:^(CGRect addedRect) { NSArray *indexPaths = [self aapl_indexPathsForElementsInRect:addedRect]; [addedIndexPaths addObjectsFromArray:indexPaths]; }]; NSArray *assetsToStartCaching = [self assetsAtIndexPaths:addedIndexPaths]; NSArray *assetsToStopCaching = [self assetsAtIndexPaths:removedIndexPaths]; // Update the assets the PHCachingImageManager is caching. [[TZImageManager manager].cachingImageManager startCachingImagesForAssets:assetsToStartCaching targetSize:AssetGridThumbnailSize contentMode:PHImageContentModeAspectFill options:nil]; [[TZImageManager manager].cachingImageManager stopCachingImagesForAssets:assetsToStopCaching targetSize:AssetGridThumbnailSize contentMode:PHImageContentModeAspectFill options:nil]; // Store the preheat rect to compare against in the future. 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) { if (indexPath.item < _models.count) { TZAssetModel *model = _models[indexPath.item]; [assets addObject:model.asset]; } } return assets; } - (NSArray *)aapl_indexPathsForElementsInRect:(CGRect)rect { NSArray *allLayoutAttributes = [_collectionView.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; } #pragma clang diagnostic pop @end @implementation TZCollectionView - (BOOL)touchesShouldCancelInContentView:(UIView *)view { if ( [view isKindOfClass:[UIControl class]]) { return YES; } return [super touchesShouldCancelInContentView:view]; } @end