// // 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" #import "TZLocationManager.h" #import #import "TZImageRequestOperation.h" @interface TZPhotoPickerController () { NSMutableArray *_models; UIView *_bottomToolBar; UIButton *_previewButton; UIButton *_doneButton; UIImageView *_numberImageView; UILabel *_numberLabel; UIButton *_originalPhotoButton; UILabel *_originalPhotoLabel; UIView *_divideLine; BOOL _shouldScrollToBottom; BOOL _showTakePhotoBtn; CGFloat _offsetItemCount; } @property CGRect previousPreheatRect; @property (nonatomic, assign) BOOL isSelectOriginalPhoto; @property (nonatomic, strong) TZCollectionView *collectionView; @property (nonatomic, strong) UILabel *noDataLabel; @property (strong, nonatomic) UICollectionViewFlowLayout *layout; @property (nonatomic, strong) UIImagePickerController *imagePickerVc; @property (strong, nonatomic) CLLocation *location; @property (nonatomic, strong) NSOperationQueue *operationQueue; @end static CGSize AssetGridThumbnailSize; static CGFloat itemMargin = 5; @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 (@available(iOS 9, *)) { 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]; self.isFirstAppear = YES; 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)]; if (tzImagePickerVc.navLeftBarButtonSettingBlock) { UIButton *leftButton = [UIButton buttonWithType:UIButtonTypeCustom]; leftButton.frame = CGRectMake(0, 0, 44, 44); [leftButton addTarget:self action:@selector(navLeftBarButtonClick) forControlEvents:UIControlEventTouchUpInside]; tzImagePickerVc.navLeftBarButtonSettingBlock(leftButton); self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:leftButton]; } else if (tzImagePickerVc.childViewControllers.count) { [tzImagePickerVc.childViewControllers firstObject].navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:[NSBundle tz_localizedStringForKey:@"Back"] style:UIBarButtonItemStylePlain target:nil action:nil]; } _showTakePhotoBtn = _model.isCameraRoll && ((tzImagePickerVc.allowTakePicture && tzImagePickerVc.allowPickingImage) || (tzImagePickerVc.allowTakeVideo && tzImagePickerVc.allowPickingVideo)); // [self resetCachedAssets]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didChangeStatusBarOrientationNotification:) name:UIApplicationDidChangeStatusBarOrientationNotification object:nil]; self.operationQueue = [[NSOperationQueue alloc] init]; self.operationQueue.maxConcurrentOperationCount = 3; } - (void)releaseObjs { for (UIView *sub in self.view.subviews) { [sub removeFromSuperview]; } [self->_models removeAllObjects]; self.collectionView = nil; self.imagePickerVc = nil; self.location = nil; [self.operationQueue cancelAllOperations]; self.operationQueue = nil; } - (void)fetchAssetModels { TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController; if (_isFirstAppear && !_model.models.count) { [tzImagePickerVc showProgressHUD]; } dispatch_async(dispatch_get_global_queue(0, 0), ^{ if (!tzImagePickerVc.sortAscendingByModificationDate && self->_isFirstAppear && self->_model.isCameraRoll) { [[TZImageManager manager] getCameraRollAlbum:tzImagePickerVc.allowPickingVideo allowPickingImage:tzImagePickerVc.allowPickingImage needFetchAssets:YES completion:^(TZAlbumModel *model) { self->_model = model; self->_models = [NSMutableArray arrayWithArray:self->_model.models]; [self initSubviews]; }]; } else { if (self->_showTakePhotoBtn || self->_isFirstAppear) { [[TZImageManager manager] getAssetsFromFetchResult:self->_model.result completion:^(NSArray *models) { self->_models = [NSMutableArray arrayWithArray:models]; [self initSubviews]; }]; } else { self->_models = [NSMutableArray arrayWithArray:self->_model.models]; [self initSubviews]; } } }); } - (void)unCheckModel:(PHAsset *)asset { NSIndexPath *ip; for (NSInteger i = 0; i < _models.count; i++) { TZAssetModel *model = _models[i]; if (model.asset == asset) { model.isChecked = NO; ip = [NSIndexPath indexPathForItem:i inSection:0]; break; } } if (ip) { [self.collectionView reloadItemsAtIndexPaths:@[ip]]; } } - (void)initSubviews { dispatch_async(dispatch_get_main_queue(), ^{ TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController; [tzImagePickerVc hideProgressHUD]; [self checkSelectedModels]; [self configCollectionView]; self->_collectionView.hidden = YES; [self configBottomToolBar]; [self scrollCollectionViewToBottom]; }); } - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController; tzImagePickerVc.isSelectOriginalPhoto = _isSelectOriginalPhoto; } - (BOOL)prefersStatusBarHidden { return NO; } - (UIStatusBarStyle)preferredStatusBarStyle { TZImagePickerController *tzImagePicker = (TZImagePickerController *)self.navigationController; if (tzImagePicker && [tzImagePicker isKindOfClass:[TZImagePickerController class]]) { return tzImagePicker.statusBarStyle; } return [super preferredStatusBarStyle]; } - (void)configCollectionView { _layout = [[UICollectionViewFlowLayout alloc] init]; _collectionView = [[TZCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:_layout]; _collectionView.backgroundColor = [UIColor whiteColor]; _collectionView.dataSource = self; _collectionView.delegate = self; _collectionView.alwaysBounceHorizontal = NO; _collectionView.contentInset = UIEdgeInsetsMake(itemMargin, itemMargin, itemMargin, itemMargin); if (_showTakePhotoBtn) { _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); if (_models.count == 0) { _noDataLabel = [UILabel new]; _noDataLabel.textAlignment = NSTextAlignmentCenter; _noDataLabel.text = [NSBundle tz_localizedStringForKey:@"No Photos or Videos"]; CGFloat rgb = 153 / 256.0; _noDataLabel.textColor = [UIColor colorWithRed:rgb green:rgb blue:rgb alpha:1.0]; _noDataLabel.font = [UIFont boldSystemFontOfSize:20]; [_collectionView addSubview:_noDataLabel]; } } [self.view addSubview:_collectionView]; [_collectionView registerClass:[TZAssetCell class] forCellWithReuseIdentifier:@"TZAssetCell"]; [_collectionView registerClass:[TZAssetCameraCell class] forCellWithReuseIdentifier:@"TZAssetCameraCell"]; } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; // 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); if (!_models) { [self fetchAssetModels]; } } - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; // [self updateCachedAssets]; UINavigationBar *bar = self.navigationController.navigationBar; if (bar.tz_top <= 0) { bar.tz_top = 20.0; } } - (void)configBottomToolBar { TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController; if (!tzImagePickerVc.showSelectBtn) return; _bottomToolBar = [[UIView alloc] initWithFrame:CGRectZero]; CGFloat rgb = 253 / 255.0; _bottomToolBar.backgroundColor = [UIColor colorWithRed:rgb green:rgb blue:rgb alpha:1.0]; _previewButton = [UIButton buttonWithType:UIButtonTypeCustom]; [_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) { _originalPhotoButton = [UIButton buttonWithType:UIButtonTypeCustom]; _originalPhotoButton.imageEdgeInsets = UIEdgeInsetsMake(0, [TZCommonTools tz_isRightToLeftLayout] ? 10 : -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:tzImagePickerVc.photoOriginDefImage forState:UIControlStateNormal]; [_originalPhotoButton setImage:tzImagePickerVc.photoOriginSelImage forState:UIControlStateSelected]; _originalPhotoButton.imageView.clipsToBounds = YES; _originalPhotoButton.imageView.contentMode = UIViewContentModeScaleAspectFit; _originalPhotoButton.selected = _isSelectOriginalPhoto; _originalPhotoButton.enabled = tzImagePickerVc.selectedModels.count > 0; _originalPhotoLabel = [[UILabel alloc] init]; _originalPhotoLabel.textAlignment = NSTextAlignmentLeft; _originalPhotoLabel.font = [UIFont systemFontOfSize:16]; _originalPhotoLabel.textColor = [UIColor blackColor]; if (_isSelectOriginalPhoto) [self getSelectedPhotoBytes]; } _doneButton = [UIButton buttonWithType:UIButtonTypeCustom]; _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:tzImagePickerVc.photoNumberIconImage]; _numberImageView.hidden = tzImagePickerVc.selectedModels.count <= 0; _numberImageView.clipsToBounds = YES; _numberImageView.contentMode = UIViewContentModeScaleAspectFit; _numberImageView.backgroundColor = [UIColor clearColor]; _numberLabel = [[UILabel alloc] init]; _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]; _divideLine = [[UIView alloc] init]; CGFloat rgb2 = 222 / 255.0; _divideLine.backgroundColor = [UIColor colorWithRed:rgb2 green:rgb2 blue:rgb2 alpha:1.0]; [_bottomToolBar addSubview:_divideLine]; [_bottomToolBar addSubview:_previewButton]; [_bottomToolBar addSubview:_doneButton]; [_bottomToolBar addSubview:_numberImageView]; [_bottomToolBar addSubview:_numberLabel]; [_bottomToolBar addSubview:_originalPhotoButton]; [self.view addSubview:_bottomToolBar]; [_originalPhotoButton addSubview:_originalPhotoLabel]; if (tzImagePickerVc.photoPickerPageUIConfigBlock) { tzImagePickerVc.photoPickerPageUIConfigBlock(_collectionView, _bottomToolBar, _previewButton, _originalPhotoButton, _originalPhotoLabel, _doneButton, _numberImageView, _numberLabel, _divideLine); } } #pragma mark - Layout - (void)viewDidLayoutSubviews { [super viewDidLayoutSubviews]; TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController; CGFloat top = 0; CGFloat collectionViewHeight = 0; CGFloat naviBarHeight = self.navigationController.navigationBar.tz_height; BOOL isStatusBarHidden = [UIApplication sharedApplication].isStatusBarHidden; CGFloat toolBarHeight = [TZCommonTools tz_isIPhoneX] ? 50 + (83 - 49) : 50; if (self.navigationController.navigationBar.isTranslucent) { top = naviBarHeight; if (!isStatusBarHidden) top += [TZCommonTools tz_statusBarHeight]; collectionViewHeight = tzImagePickerVc.showSelectBtn ? self.view.tz_height - toolBarHeight - top : self.view.tz_height - top;; } else { collectionViewHeight = tzImagePickerVc.showSelectBtn ? self.view.tz_height - toolBarHeight : self.view.tz_height; } _collectionView.frame = CGRectMake(0, top, self.view.tz_width, collectionViewHeight); _noDataLabel.frame = _collectionView.bounds; CGFloat itemWH = (self.view.tz_width - (self.columnNumber + 1) * itemMargin) / self.columnNumber; _layout.itemSize = CGSizeMake(itemWH, itemWH); _layout.minimumInteritemSpacing = itemMargin; _layout.minimumLineSpacing = itemMargin; [_collectionView setCollectionViewLayout:_layout]; if (_offsetItemCount > 0) { CGFloat offsetY = _offsetItemCount * (_layout.itemSize.height + _layout.minimumLineSpacing); [_collectionView setContentOffset:CGPointMake(0, offsetY)]; } CGFloat toolBarTop = 0; if (!self.navigationController.navigationBar.isHidden) { toolBarTop = self.view.tz_height - toolBarHeight; } else { CGFloat navigationHeight = naviBarHeight + [TZCommonTools tz_statusBarHeight]; toolBarTop = self.view.tz_height - toolBarHeight - navigationHeight; } _bottomToolBar.frame = CGRectMake(0, toolBarTop, self.view.tz_width, toolBarHeight); 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.frame = CGRectMake(10, 3, previewWidth, 44); _previewButton.tz_width = !tzImagePickerVc.showSelectBtn ? 0 : previewWidth; 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.frame = CGRectMake(CGRectGetMaxX(_previewButton.frame), 0, fullImageWidth + 56, 50); _originalPhotoLabel.frame = CGRectMake(fullImageWidth + 46, 0, 80, 50); } [_doneButton sizeToFit]; _doneButton.frame = CGRectMake(self.view.tz_width - _doneButton.tz_width - 12, 0, _doneButton.tz_width, 50); _numberImageView.frame = CGRectMake(_doneButton.tz_left - 24 - 5, 13, 24, 24); _numberLabel.frame = _numberImageView.frame; _divideLine.frame = CGRectMake(0, 0, self.view.tz_width, 1); [TZImageManager manager].columnNumber = [TZImageManager manager].columnNumber; [TZImageManager manager].photoWidth = tzImagePickerVc.photoWidth; [self.collectionView reloadData]; if (tzImagePickerVc.photoPickerPageDidLayoutSubviewsBlock) { tzImagePickerVc.photoPickerPageDidLayoutSubviewsBlock(_collectionView, _bottomToolBar, _previewButton, _originalPhotoButton, _originalPhotoLabel, _doneButton, _numberImageView, _numberLabel, _divideLine); } } #pragma mark - Notification - (void)didChangeStatusBarOrientationNotification:(NSNotification *)noti { _offsetItemCount = _collectionView.contentOffset.y / (_layout.itemSize.height + _layout.minimumLineSpacing); } #pragma mark - Click Event - (void)navLeftBarButtonClick{ [self.navigationController popViewControllerAnimated:YES]; } - (void)previewButtonClick { TZPhotoPreviewController *photoPreviewVc = [[TZPhotoPreviewController alloc] init]; [self pushPhotoPrevireViewController:photoPreviewVc needCheckSelectedModels:YES]; } - (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 *assets = [NSMutableArray array]; NSMutableArray *photos; NSMutableArray *infoArr; if (tzImagePickerVc.onlyReturnAsset) { // not fetch image for (NSInteger i = 0; i < tzImagePickerVc.selectedModels.count; i++) { TZAssetModel *model = tzImagePickerVc.selectedModels[i]; [assets addObject:model.asset]; } } else { // fetch image photos = [NSMutableArray array]; 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; __block UIAlertController *alertView; for (NSInteger i = 0; i < tzImagePickerVc.selectedModels.count; i++) { TZAssetModel *model = tzImagePickerVc.selectedModels[i]; TZImageRequestOperation *operation = [[TZImageRequestOperation alloc] initWithAsset:model.asset completion:^(UIImage * _Nonnull photo, NSDictionary * _Nonnull info, BOOL isDegraded) { if (isDegraded) return; if (photo) { if (![TZImagePickerConfig sharedInstance].notScaleImage) { photo = [[TZImageManager manager] 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) { [tzImagePickerVc hideAlertView:alertView]; [self didGetAllPhotos:photos assets:assets infoArr:infoArr]; } } progressHandler:^(double progress, NSError * _Nonnull error, BOOL * _Nonnull stop, NSDictionary * _Nonnull info) { // 如果图片正在从iCloud同步中,提醒用户 if (progress < 1 && havenotShowAlert && !alertView) { [tzImagePickerVc hideProgressHUD]; alertView = [tzImagePickerVc showAlertWithTitle:[NSBundle tz_localizedStringForKey:@"Synchronizing photos from iCloud"]]; havenotShowAlert = NO; return; } if (progress >= 1) { havenotShowAlert = YES; } }]; [self.operationQueue addOperation:operation]; } } if (tzImagePickerVc.selectedModels.count <= 0 || tzImagePickerVc.onlyReturnAsset) { [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.allowPickingVideo && tzImagePickerVc.maxImagesCount == 1) { if ([[TZImageManager manager] isVideo:[assets firstObject]]) { if ([tzImagePickerVc.pickerDelegate respondsToSelector:@selector(imagePickerController:didFinishPickingVideo:sourceAssets:)]) { [tzImagePickerVc.pickerDelegate imagePickerController:tzImagePickerVc didFinishPickingVideo:[photos firstObject] sourceAssets:[assets firstObject]]; } if (tzImagePickerVc.didFinishPickingVideoHandle) { tzImagePickerVc.didFinishPickingVideoHandle([photos firstObject], [assets firstObject]); } return; } } 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) { 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.item >= _models.count) || (!tzImagePickerVc.sortAscendingByModificationDate && indexPath.item == 0)) && _showTakePhotoBtn) { TZAssetCameraCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"TZAssetCameraCell" forIndexPath:indexPath]; cell.imageView.image = tzImagePickerVc.takePictureImage; if ([tzImagePickerVc.takePictureImageName isEqualToString:@"takePicture80"]) { cell.imageView.contentMode = UIViewContentModeCenter; CGFloat rgb = 223 / 255.0; cell.imageView.backgroundColor = [UIColor colorWithRed:rgb green:rgb blue:rgb alpha:1.0]; } else { cell.imageView.backgroundColor = [UIColor colorWithWhite:1.000 alpha:0.500]; } return cell; } // the cell dipaly photo or video / 展示照片或视频的cell TZAssetCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"TZAssetCell" forIndexPath:indexPath]; cell.allowPickingMultipleVideo = tzImagePickerVc.allowPickingMultipleVideo; cell.photoDefImage = tzImagePickerVc.photoDefImage; cell.photoSelImage = tzImagePickerVc.photoSelImage; cell.assetCellDidSetModelBlock = tzImagePickerVc.assetCellDidSetModelBlock; cell.assetCellDidLayoutSubviewsBlock = tzImagePickerVc.assetCellDidLayoutSubviewsBlock; TZAssetModel *model; if (tzImagePickerVc.sortAscendingByModificationDate || !_showTakePhotoBtn) { model = _models[indexPath.item]; } else { model = _models[indexPath.item - 1]; } cell.allowPickingGif = tzImagePickerVc.allowPickingGif; cell.model = model; if (model.isSelected && tzImagePickerVc.showSelectedIndex) { cell.index = [tzImagePickerVc.selectedAssetIds indexOfObject:model.asset.localIdentifier] + 1; } cell.showSelectBtn = tzImagePickerVc.showSelectBtn; cell.allowPreview = tzImagePickerVc.allowPreview; if (tzImagePickerVc.selectedModels.count >= tzImagePickerVc.maxImagesCount && tzImagePickerVc.showPhotoCannotSelectLayer && !model.isSelected) { cell.cannotSelectLayerButton.backgroundColor = tzImagePickerVc.cannotSelectLayerColor; cell.cannotSelectLayerButton.hidden = NO; } else { cell.cannotSelectLayerButton.hidden = YES; } __weak typeof(cell) weakCell = cell; __weak typeof(self) weakSelf = self; __weak typeof(_numberImageView.layer) weakLayer = _numberImageView.layer; cell.didSelectPhotoBlock = ^(BOOL isSelected) { __strong typeof(weakCell) strongCell = weakCell; __strong typeof(weakSelf) strongSelf = weakSelf; __strong typeof(weakLayer) strongLayer = weakLayer; TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)strongSelf.navigationController; // 1. cancel select / 取消选择 if (isSelected) { strongCell.selectPhotoButton.selected = NO; model.isSelected = NO; NSArray *selectedModels = [NSArray arrayWithArray:tzImagePickerVc.selectedModels]; for (TZAssetModel *model_item in selectedModels) { if ([model.asset.localIdentifier isEqualToString:model_item.asset.localIdentifier]) { [tzImagePickerVc removeSelectedModel:model_item]; break; } } [strongSelf refreshBottomToolBarStatus]; if (tzImagePickerVc.showSelectedIndex || tzImagePickerVc.showPhotoCannotSelectLayer) { [[NSNotificationCenter defaultCenter] postNotificationName:@"TZ_PHOTO_PICKER_RELOAD_NOTIFICATION" object:strongSelf.navigationController]; } [UIView showOscillatoryAnimationWithLayer:strongLayer type:TZOscillatoryAnimationToSmaller]; } else { // 2. select:check if over the maxImagesCount / 选择照片,检查是否超过了最大个数的限制 if (tzImagePickerVc.selectedModels.count < tzImagePickerVc.maxImagesCount) { if (tzImagePickerVc.maxImagesCount == 1 && !tzImagePickerVc.allowPreview) { model.isSelected = YES; [strongCell toggleCheck:YES]; [tzImagePickerVc addSelectedModel:model]; [strongSelf doneButtonClick]; return; } strongCell.selectPhotoButton.selected = YES; model.isSelected = YES; [tzImagePickerVc addSelectedModel:model]; if (tzImagePickerVc.showSelectedIndex || tzImagePickerVc.showPhotoCannotSelectLayer) { [[NSNotificationCenter defaultCenter] postNotificationName:@"TZ_PHOTO_PICKER_RELOAD_NOTIFICATION" object:strongSelf.navigationController]; } [strongSelf refreshBottomToolBarStatus]; [UIView showOscillatoryAnimationWithLayer:strongLayer type:TZOscillatoryAnimationToSmaller]; } else { if (self.isSpotMode) { return; } NSString *title = [NSString stringWithFormat:[NSBundle tz_localizedStringForKey:@"Select a maximum of %zd photos"], tzImagePickerVc.maxImagesCount]; [tzImagePickerVc showAlertWithTitle:title]; } } }; cell.didLongPressPhotoBlock = ^(TZAssetModel *model) { __strong typeof(weakCell) strongCell = weakCell; TZVideoPlayerController *videoPlayerVc = [[TZVideoPlayerController alloc] init]; videoPlayerVc.currentCell = strongCell; [weakSelf.navigationController pushViewController:videoPlayerVc animated:YES]; }; return cell; } - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath { // take a photo / 去拍照 TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController; if (((tzImagePickerVc.sortAscendingByModificationDate && indexPath.item >= _models.count) || (!tzImagePickerVc.sortAscendingByModificationDate && indexPath.item == 0)) && _showTakePhotoBtn) { [self takePhoto]; return; } // preview phote or video / 预览照片或视频 NSInteger index = indexPath.item; if (!tzImagePickerVc.sortAscendingByModificationDate && _showTakePhotoBtn) { index = indexPath.item - 1; } TZAssetModel *model = _models[index]; if (model.type == TZAssetModelMediaTypeVideo && !tzImagePickerVc.allowPickingMultipleVideo) { if (tzImagePickerVc.selectedModels.count > 0) { TZImagePickerController *imagePickerVc = (TZImagePickerController *)self.navigationController; [imagePickerVc showAlertWithTitle:[NSBundle tz_localizedStringForKey:@"Can not choose both video and photo"]]; } else { /// 在Cell imageView中添加了longPress 手势来触发视频预览 // TZVideoPlayerController *videoPlayerVc = [[TZVideoPlayerController alloc] init]; // videoPlayerVc.model = model; // [self.navigationController pushViewController:videoPlayerVc animated:YES]; } } else if (model.type == TZAssetModelMediaTypePhotoGif && tzImagePickerVc.allowPickingGif && !tzImagePickerVc.allowPickingMultipleVideo) { 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 { // [self updateCachedAssets]; } #pragma mark - Private Method /// 拍照按钮点击事件 - (void)takePhoto { AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo]; if ((authStatus == AVAuthorizationStatusRestricted || authStatus ==AVAuthorizationStatusDenied)) { NSDictionary *infoDict = [TZCommonTools tz_getInfoDictionary]; // 无权限 做一个友好的提示 NSString *appName = [infoDict valueForKey:@"CFBundleDisplayName"]; if (!appName) appName = [infoDict 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 if (authStatus == AVAuthorizationStatusNotDetermined) { // fix issue 466, 防止用户首次拍照拒绝授权时相机页黑屏 [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) { if (granted) { dispatch_async(dispatch_get_main_queue(), ^{ [self pushImagePickerController]; }); } }]; } else { [self pushImagePickerController]; } } // 调用相机 - (void)pushImagePickerController { // 提前定位 TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController; if (tzImagePickerVc.allowCameraLocation) { __weak typeof(self) weakSelf = self; [[TZLocationManager manager] startLocationWithSuccessBlock:^(NSArray *locations) { __strong typeof(weakSelf) strongSelf = weakSelf; strongSelf.location = [locations firstObject]; } failureBlock:^(NSError *error) { __strong typeof(weakSelf) strongSelf = weakSelf; strongSelf.location = nil; }]; } UIImagePickerControllerSourceType sourceType = UIImagePickerControllerSourceTypeCamera; if ([UIImagePickerController isSourceTypeAvailable: sourceType]) { self.imagePickerVc.sourceType = sourceType; NSMutableArray *mediaTypes = [NSMutableArray array]; if (tzImagePickerVc.allowTakePicture) { [mediaTypes addObject:(NSString *)kUTTypeImage]; } if (tzImagePickerVc.allowTakeVideo) { [mediaTypes addObject:(NSString *)kUTTypeMovie]; self.imagePickerVc.videoMaximumDuration = tzImagePickerVc.videoMaximumDuration; } self.imagePickerVc.mediaTypes= mediaTypes; if (tzImagePickerVc.uiImagePickerControllerSettingBlock) { tzImagePickerVc.uiImagePickerControllerSettingBlock(_imagePickerVc); } [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]; if (tzImagePickerVc.photoPickerPageDidRefreshStateBlock) { tzImagePickerVc.photoPickerPageDidRefreshStateBlock(_collectionView, _bottomToolBar, _previewButton, _originalPhotoButton, _originalPhotoLabel, _doneButton, _numberImageView, _numberLabel, _divideLine);; } } - (void)pushPhotoPrevireViewController:(TZPhotoPreviewController *)photoPreviewVc { [self pushPhotoPrevireViewController:photoPreviewVc needCheckSelectedModels:NO]; } - (void)pushPhotoPrevireViewController:(TZPhotoPreviewController *)photoPreviewVc needCheckSelectedModels:(BOOL)needCheckSelectedModels { __weak typeof(self) weakSelf = self; photoPreviewVc.isSelectOriginalPhoto = _isSelectOriginalPhoto; [photoPreviewVc setBackButtonClickBlock:^(BOOL isSelectOriginalPhoto) { __strong typeof(weakSelf) strongSelf = weakSelf; strongSelf.isSelectOriginalPhoto = isSelectOriginalPhoto; if (needCheckSelectedModels) { [strongSelf checkSelectedModels]; } [strongSelf.collectionView reloadData]; [strongSelf refreshBottomToolBarStatus]; }]; [photoPreviewVc setDoneButtonClickBlock:^(BOOL isSelectOriginalPhoto) { __strong typeof(weakSelf) strongSelf = weakSelf; strongSelf.isSelectOriginalPhoto = isSelectOriginalPhoto; [strongSelf doneButtonClick]; }]; [photoPreviewVc setDoneButtonClickBlockCropMode:^(UIImage *cropedImage, id asset) { __strong typeof(weakSelf) strongSelf = weakSelf; [strongSelf didGetAllPhotos:@[cropedImage] assets:@[asset] infoArr:nil]; }]; [self.navigationController pushViewController:photoPreviewVc animated:YES]; } - (void)getSelectedPhotoBytes { // 越南语 && 5屏幕时会显示不下,暂时这样处理 if ([[TZImagePickerConfig sharedInstance].preferredLanguage isEqualToString:@"vi"] && self.view.tz_width <= 320) { return; } TZImagePickerController *imagePickerVc = (TZImagePickerController *)self.navigationController; [[TZImageManager manager] getPhotosBytesWithArray:imagePickerVc.selectedModels completion:^(NSString *totalBytes) { self->_originalPhotoLabel.text = [NSString stringWithFormat:@"(%@)",totalBytes]; }]; } - (void)scrollCollectionViewToBottom { TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController; if (_shouldScrollToBottom && _models.count > 0) { NSInteger item = 0; if (tzImagePickerVc.sortAscendingByModificationDate) { item = _models.count - 1; if (_showTakePhotoBtn) { item += 1; } } dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.01 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [self->_collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:item inSection:0] atScrollPosition:UICollectionViewScrollPositionBottom animated:NO]; self->_shouldScrollToBottom = NO; self->_collectionView.hidden = NO; }); } else { _collectionView.hidden = NO; } } - (void)checkSelectedModels { NSMutableArray *selectedAssets = [NSMutableArray array]; TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController; for (TZAssetModel *model in tzImagePickerVc.selectedModels) { [selectedAssets addObject:model.asset]; } for (TZAssetModel *model in _models) { model.isSelected = NO; if ([selectedAssets containsObject:model.asset]) { model.isSelected = YES; } } } #pragma mark - UIAlertViewDelegate - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex { if (buttonIndex == 1) { // 去设置界面,开启相机访问权限 [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]]; } } #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 *photo = [info objectForKey:UIImagePickerControllerOriginalImage]; if (photo) { [[TZImageManager manager] savePhotoWithImage:photo location:self.location completion:^(PHAsset *asset, NSError *error){ if (!error) { [self addPHAsset:asset]; } }]; self.location = nil; } } else if ([type isEqualToString:@"public.movie"]) { // TZImagePickerController *imagePickerVc = (TZImagePickerController *)self.navigationController; // [imagePickerVc showProgressHUD]; NSURL *videoUrl = [info objectForKey:UIImagePickerControllerMediaURL]; if (videoUrl) { [[TZImageManager manager] saveVideoWithUrl:videoUrl location:self.location completion:^(PHAsset *asset, NSError *error) { if (!error) { [self addPHAsset:asset]; } }]; self.location = nil; } } } - (void)addPHAsset:(PHAsset *)asset { TZAssetModel *assetModel = [[TZImageManager manager] createModelWithAsset:asset]; TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController; [tzImagePickerVc hideProgressHUD]; if (tzImagePickerVc.sortAscendingByModificationDate) { [_models addObject:assetModel]; } else { [_models insertObject:assetModel atIndex:0]; } if (tzImagePickerVc.maxImagesCount <= 1) { if (tzImagePickerVc.allowCrop && asset.mediaType == PHAssetMediaTypeImage) { TZPhotoPreviewController *photoPreviewVc = [[TZPhotoPreviewController alloc] init]; if (tzImagePickerVc.sortAscendingByModificationDate) { photoPreviewVc.currentIndex = _models.count - 1; } else { photoPreviewVc.currentIndex = 0; } photoPreviewVc.models = _models; [self pushPhotoPrevireViewController:photoPreviewVc]; } else { [tzImagePickerVc addSelectedModel:assetModel]; [self doneButtonClick]; } return; } if (tzImagePickerVc.selectedModels.count < tzImagePickerVc.maxImagesCount) { if (assetModel.type == TZAssetModelMediaTypeVideo && !tzImagePickerVc.allowPickingMultipleVideo) { // 不能多选视频的情况下,不选中拍摄的视频 } else { assetModel.isSelected = YES; [tzImagePickerVc addSelectedModel:assetModel]; [self refreshBottomToolBarStatus]; } } _collectionView.hidden = YES; [_collectionView reloadData]; _shouldScrollToBottom = YES; [self scrollCollectionViewToBottom]; } - (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker { [picker dismissViewControllerAnimated:YES completion:nil]; } - (void)dealloc { NSLog(@"#🐣#: release %@", [self class]); } #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]; } - (void)dealloc { NSLog(@"🐣release🐣: TZCollectionView ==== %@", self); } @end