RITLPhotosCollectionViewController.m 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799
  1. //
  2. // RITLPhotosItemsCollectionViewController.m
  3. // RITLPhotoDemo
  4. //
  5. // Created by YueWen on 2018/3/7.
  6. // Copyright © 2018年 YueWen. All rights reserved.
  7. //
  8. #import "RITLPhotosCollectionViewController.h"
  9. #import "RITLPhotosPreviewController.h"
  10. #import "RITLPhotosHorBrowseViewController.h"
  11. #import "RITLPhotosCell.h"
  12. #import "RITLPhotosBottomView.h"
  13. #import "RITLPhotosBrowseAllDataSource.h"
  14. #import "RITLPhotosBrowseDataSource.h"
  15. #import "RITLPhotosConfiguration.h"
  16. #import "PHAsset+RITLPhotos.h"
  17. #import "NSString+RITLPhotos.h"
  18. #import "PHPhotoLibrary+RITLPhotoStore.h"
  19. #import "UICollectionView+RITLIndexPathsForElements.h"
  20. #import "UICollectionViewCell+RITLPhotosAsset.h"
  21. #import "RITLKit.h"
  22. #import "UIView+RITLFrameChanged.h"
  23. #import "Masonry.h"
  24. #import <Photos/Photos.h>
  25. #import "UIImage+Tint.h"
  26. //Data
  27. #import "RITLPhotosMaker.h"
  28. #import "RITLPhotosDataManager.h"
  29. #import "RITLPhotosConfiguration.h"
  30. typedef NSString RITLDifferencesKey;
  31. static RITLDifferencesKey *const RITLDifferencesKeyAdded = @"RITLDifferencesKeyAdded";
  32. static RITLDifferencesKey *const RITLDifferencesKeyRemoved = @"RITLDifferencesKeyRemoved";
  33. @interface UICollectionView (RITLPhotosCollectionViewController)
  34. - (NSArray <NSIndexPath *>*)indexPathsForElementsInRect:(CGRect)rect;
  35. @end
  36. @interface RITLPhotosCollectionViewController ()<UICollectionViewDataSource,
  37. UICollectionViewDelegateFlowLayout,
  38. UIViewControllerPreviewingDelegate,
  39. UICollectionViewDataSourcePrefetching,
  40. RITLPhotosCellActionTarget>
  41. // Library
  42. @property (nonatomic, strong) PHPhotoLibrary *photoLibrary;
  43. // Datamanager
  44. @property (nonatomic, strong) RITLPhotosDataManager *dataManager;
  45. // data
  46. @property (nonatomic, strong) UICollectionView *collectionView;
  47. @property (nonatomic, strong) PHAssetCollection *assetCollection;
  48. @property (nonatomic, strong) PHFetchResult <PHAsset *> *assets;
  49. @property (nonatomic, strong) PHCachingImageManager* imageManager;
  50. @property (nonatomic, assign) CGRect previousPreheatRect;
  51. // view
  52. @property (nonatomic, strong) RITLPhotosBottomView *bottomView;
  53. @property (nonatomic, strong) UILabel *tintLable; // 无权限提醒
  54. @property (nonatomic, assign) BOOL isEdit;
  55. @property (nonatomic, strong) PHAsset *oldAsset;
  56. @property (nonatomic, assign) BOOL isScroll;
  57. @end
  58. @implementation RITLPhotosCollectionViewController
  59. static NSString *const reuseIdentifier = @"photo";
  60. + (RITLPhotosCollectionViewController *)photosCollectionController
  61. {
  62. return RITLPhotosCollectionViewController.new;
  63. }
  64. - (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
  65. {
  66. if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
  67. self.previousPreheatRect = CGRectZero;
  68. self.dataManager = [RITLPhotosDataManager sharedInstance];
  69. }
  70. return self;
  71. }
  72. - (void)resetCachedAssets
  73. {
  74. [self.imageManager stopCachingImagesForAllAssets];
  75. self.previousPreheatRect = CGRectZero;
  76. }
  77. - (PHAssetCollection *)assetCollection
  78. {
  79. if (!_assetCollection) {
  80. _assetCollection = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum subtype:PHAssetCollectionSubtypeSmartAlbumUserLibrary options:nil].firstObject;
  81. }
  82. return _assetCollection;
  83. }
  84. - (void)viewDidLoad {
  85. [super viewDidLoad];
  86. self.view.backgroundColor = UIColor.whiteColor;
  87. //设置navigationController 背景颜色
  88. UIImage *iv = [[UIImage alloc] init];
  89. if (THESIMPLESTYLE) {
  90. iv = [[UIImage imageNamed:@"navBarBackground"] imageWithTintColor:[UIColor whiteColor]];
  91. }else {
  92. iv = [g_theme themeTintImage:@"navBarBackground"];
  93. }
  94. [self.navigationController.navigationBar setBackgroundImage:iv forBarMetrics:UIBarMetricsDefault];
  95. [self.navigationController.navigationBar setTintColor:[UIColor whiteColor]];
  96. //进行KVO观察
  97. [self.dataManager addObserver:self forKeyPath:@"count" options:NSKeyValueObservingOptionNew context:nil];
  98. [self.dataManager addObserver:self forKeyPath:@"hightQuality" options:NSKeyValueObservingOptionNew context:nil];
  99. // NavigationItem
  100. UIButton *btn1 = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 32, 18)];
  101. [btn1 setTitle:Localized(@"JX_Cencal") forState:UIControlStateNormal];
  102. [btn1 setTitleColor:HEXCOLOR(0x333333) forState:UIControlStateNormal];
  103. [btn1.titleLabel setFont:SYSFONT(15)];
  104. [btn1 addTarget:self action:@selector(actionQuit) forControlEvents:UIControlEventTouchUpInside];
  105. UIBarButtonItem *barBtn1 = [[UIBarButtonItem alloc] initWithCustomView:btn1];
  106. self.navigationItem.rightBarButtonItem = barBtn1;
  107. UIButton *btn = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 18, 18)];
  108. [btn setBackgroundImage:[UIImage imageNamed:@"title_back_black"] forState:UIControlStateNormal];
  109. [btn addTarget:self action:@selector(actionQuit) forControlEvents:UIControlEventTouchUpInside];
  110. UIBarButtonItem *barBtn = [[UIBarButtonItem alloc] initWithCustomView:btn];
  111. self.navigationItem.leftBarButtonItem = barBtn;
  112. // Register cell classes
  113. [self.collectionView registerClass:[RITLPhotosCell class] forCellWithReuseIdentifier:reuseIdentifier];
  114. self.bottomView = RITLPhotosBottomView.new;
  115. self.bottomView.previewButton.enabled = false;
  116. self.bottomView.sendButton.enabled = false;
  117. [self.bottomView.previewButton addTarget:self
  118. action:@selector(pushPreviewViewController)
  119. forControlEvents:UIControlEventTouchUpInside];
  120. [self.bottomView.fullImageButton addTarget:self
  121. action:@selector(hightQualityShouldChanged:)
  122. forControlEvents:UIControlEventTouchUpInside];
  123. [self.bottomView.sendButton addTarget:self
  124. action:@selector(pushPhotosMaker)
  125. forControlEvents:UIControlEventTouchUpInside];
  126. self.bottomView.backgroundColor = [UIColor.blackColor colorWithAlphaComponent:0.8];
  127. // Do any additional setup after loading the view.
  128. [self.view addSubview:self.collectionView];
  129. [self.view addSubview:self.bottomView];
  130. [self.view addSubview:self.tintLable];
  131. // Layout
  132. [self.bottomView mas_makeConstraints:^(MASConstraintMaker *make) {
  133. make.bottom.left.right.offset(0);
  134. make.height.mas_equalTo(RITL_DefaultTabBarHeight - 3);
  135. }];
  136. [self.collectionView mas_makeConstraints:^(MASConstraintMaker *make) {
  137. make.left.top.right.offset(0);
  138. make.bottom.equalTo(self.bottomView.mas_top).offset(0);
  139. }];
  140. [self.tintLable mas_makeConstraints:^(MASConstraintMaker *make) {
  141. make.top.offset(JX_SCREEN_TOP+20);
  142. make.left.offset(30);
  143. make.right.offset(-30);
  144. make.height.mas_equalTo(60);
  145. }];
  146. //加载数据
  147. if (self.localIdentifier) {
  148. //加载
  149. self.assetCollection = [PHAssetCollection fetchAssetCollectionsWithLocalIdentifiers:@[self.localIdentifier] options:nil].firstObject;
  150. }
  151. //进行权限检测
  152. [PHPhotoLibrary authorizationStatusAllow:^{
  153. self.imageManager = [PHCachingImageManager new];
  154. self.photoLibrary = PHPhotoLibrary.sharedPhotoLibrary;
  155. [self resetCachedAssets];
  156. self.assets = [PHAsset fetchAssetsInAssetCollection:self.assetCollection options:nil];
  157. //reload
  158. self.collectionView.hidden = true;
  159. [self.collectionView reloadData];
  160. // 拥有照片访问权限
  161. self.tintLable.hidden = YES;
  162. self.bottomView.hidden = NO;
  163. //设置itemTitle
  164. // self.navigationItem.title = self.assetCollection.localizedTitle;
  165. self.navigationItem.title = Localized(@"JX_Photo");
  166. [self.navigationController.navigationBar setTitleTextAttributes:@{NSForegroundColorAttributeName:[UIColor whiteColor]}];
  167. //计算行数,并滑动到最后一行
  168. self.collectionView.hidden = false;
  169. [self collectionViewScrollToBottomAnimatedNoneHandler:^NSInteger(NSInteger row) {
  170. return row;
  171. }];
  172. // 滚动到底部
  173. [self.collectionView setContentOffset:CGPointMake(0, self.collectionView.contentSize.height - self.collectionView.frame.size.height) animated:YES];
  174. } denied:^{
  175. // 没有照片访问权限
  176. self.tintLable.hidden = NO;
  177. self.bottomView.hidden = YES;
  178. }];
  179. [self changedBottomViewStatus];
  180. }
  181. - (void)actionQuit {
  182. [self dismissPhotoControllers];
  183. }
  184. - (void)dealloc
  185. {
  186. if (self.isViewLoaded) {
  187. [self.dataManager removeObserver:self forKeyPath:@"count"];
  188. [self.dataManager removeObserver:self forKeyPath:@"hightQuality"];
  189. }
  190. [self.dataManager removeAllPHAssets];
  191. NSLog(@"[%@] is dealloc",NSStringFromClass(self.class));
  192. }
  193. - (void)collectionViewScrollToBottomAnimatedNoneHandler:(NSInteger(^)(NSInteger row))handler
  194. {
  195. //获得所有的数据个数
  196. NSInteger itemCount = self.assets.count;
  197. if (itemCount < 4) { return; }
  198. //获得行数
  199. NSInteger row = itemCount % 4 == 0 ? itemCount / 4 : itemCount / 4 + 1;
  200. if (handler) { row = handler(row); }
  201. //item
  202. CGFloat itemHeight = (RITL_SCREEN_WIDTH - 3.0f * 3) / 4;
  203. //进行高度换算
  204. CGFloat height = row * itemHeight;
  205. height += 3.0 * (row - 1);
  206. //扩展contentSize
  207. self.collectionView.ritl_contentSizeHeight = height;
  208. //底部bottom
  209. CGFloat bottomHeight = RITL_DefaultTabBarHeight;
  210. //可以显示的区域
  211. CGFloat showSapce = RITL_SCREEN_HEIGHT - RITL_DefaultNaviBarHeight - bottomHeight;
  212. //进行单位换算
  213. self.collectionView.ritl_contentOffSetY = MIN(MAX(0,height - showSapce),-1 * RITL_DefaultNaviBarHeight);
  214. }
  215. - (void)viewDidAppear:(BOOL)animated
  216. {
  217. [super viewDidAppear:animated];
  218. // if (self.isEdit) {
  219. // self.imageManager = [PHCachingImageManager new];
  220. // self.photoLibrary = PHPhotoLibrary.sharedPhotoLibrary;
  221. //
  222. // [self resetCachedAssets];
  223. //
  224. // self.assets = [PHAsset fetchAssetsInAssetCollection:self.assetCollection options:nil];
  225. // [self.collectionView reloadData];
  226. // if (self.collectionView.contentOffset.y < self.collectionView.contentSize.height) {
  227. // dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  228. // // 滚动到底部
  229. // [self.collectionView setContentOffset:CGPointMake(0, self.collectionView.contentSize.height - self.collectionView.frame.size.height) animated:NO];
  230. // });
  231. // }
  232. // }
  233. }
  234. - (void)didReceiveMemoryWarning {
  235. [super didReceiveMemoryWarning];
  236. // Dispose of any resources that can be recreated.
  237. }
  238. #pragma mark - *************** cache ***************
  239. - (void)updateCachedAssets
  240. {
  241. if (!self.isViewLoaded || self.view.window == nil) { return; }
  242. //没有权限,关闭
  243. if (PHPhotoLibrary.authorizationStatus != PHAuthorizationStatusAuthorized) { return; }
  244. //可视化
  245. CGRect visibleRect = CGRectMake(self.collectionView.ritl_contentOffSetX, self.collectionView.ritl_contentOffSetY, self.collectionView.ritl_width, self.collectionView.ritl_height);
  246. //进行拓展
  247. CGRect preheatRect = CGRectInset(visibleRect, 0, -0.5 * visibleRect.size.height);
  248. //只有可视化的区域与之前的区域有显著的区域变化才需要更新
  249. CGFloat delta = ABS(CGRectGetMidY(preheatRect) - CGRectGetMidY(self.previousPreheatRect));
  250. if (delta <= self.view.ritl_height / 3.0) { return; }
  251. //获得比较后需要进行预加载以及需要停止缓存的区域
  252. NSDictionary *differences = [self differencesBetweenRects:self.previousPreheatRect new:preheatRect];
  253. NSArray <NSValue *> *addedRects = differences[RITLDifferencesKeyAdded];
  254. NSArray <NSValue *> *removedRects = differences[RITLDifferencesKeyRemoved];
  255. ///进行提前缓存的资源
  256. NSArray <PHAsset *> *addedAssets = [[[addedRects ritl_map:^id _Nonnull(NSValue * _Nonnull rectValue) {
  257. return [self.collectionView indexPathsForElementsInRect:rectValue.CGRectValue];
  258. }] ritl_reduce:@[] reduceHandler:^NSArray * _Nonnull(NSArray * _Nonnull result, NSArray <NSIndexPath *>*_Nonnull items) {
  259. return [result arrayByAddingObjectsFromArray:items];
  260. }] ritl_map:^id _Nonnull(NSIndexPath *_Nonnull index) {
  261. return [self.assets objectAtIndex:index.item];
  262. }];
  263. ///提前停止缓存的资源
  264. NSArray <PHAsset *> *removedAssets = [[[removedRects ritl_map:^id _Nonnull(NSValue * _Nonnull rectValue) {
  265. return [self.collectionView indexPathsForElementsInRect:rectValue.CGRectValue];
  266. }] ritl_reduce:@[] reduceHandler:^NSArray * _Nonnull(NSArray * _Nonnull result, NSArray <NSIndexPath *>* _Nonnull items) {
  267. return [result arrayByAddingObjectsFromArray:items];
  268. }] ritl_map:^id _Nonnull(NSIndexPath *_Nonnull index) {
  269. return [self.assets objectAtIndex:index.item];
  270. }];
  271. CGSize thimbnailSize = [self collectionView:self.collectionView layout:self.collectionView.collectionViewLayout sizeForItemAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]];
  272. //更新缓存
  273. [self.imageManager startCachingImagesForAssets:addedAssets targetSize:thimbnailSize contentMode:PHImageContentModeAspectFill options:nil];
  274. [self.imageManager stopCachingImagesForAssets:removedAssets targetSize:thimbnailSize contentMode:PHImageContentModeAspectFill options:nil];
  275. //记录当前位置
  276. self.previousPreheatRect = preheatRect;
  277. }
  278. - (NSDictionary <RITLDifferencesKey*,NSArray<NSValue *>*> *)differencesBetweenRects:(CGRect)old new:(CGRect)new
  279. {
  280. if (CGRectIntersectsRect(old, new)) {//如果区域交叉
  281. NSMutableArray <NSValue *> * added = [NSMutableArray arrayWithCapacity:10];
  282. if (CGRectGetMaxY(new) > CGRectGetMaxY(old)) {//表示上拉
  283. [added addObject:[NSValue valueWithCGRect:CGRectMake(new.origin.x, CGRectGetMaxY(old), new.size.width, CGRectGetMaxY(new) - CGRectGetMaxY(old))]];
  284. }
  285. if(CGRectGetMinY(old) > CGRectGetMinY(new)){//表示下拉
  286. [added addObject:[NSValue valueWithCGRect:CGRectMake(new.origin.x, CGRectGetMinY(new), new.size.width, CGRectGetMinY(old) - CGRectGetMinY(new))]];
  287. }
  288. NSMutableArray <NSValue *> * removed = [NSMutableArray arrayWithCapacity:10];
  289. if (CGRectGetMaxY(new) < CGRectGetMaxY(old)) {//表示下拉
  290. [removed addObject:[NSValue valueWithCGRect:CGRectMake(new.origin.x, CGRectGetMaxY(new), new.size.width, CGRectGetMaxY(old) - CGRectGetMaxY(new))]];
  291. }
  292. if (CGRectGetMinY(old) < CGRectGetMinY(new)) {//表示上拉
  293. [removed addObject:[NSValue valueWithCGRect:CGRectMake(new.origin.x, CGRectGetMinY(old), new.size.width, CGRectGetMinY(new) - CGRectGetMinY(old))]];
  294. }
  295. return @{RITLDifferencesKeyAdded:added,
  296. RITLDifferencesKeyRemoved:removed};
  297. }else {
  298. return @{RITLDifferencesKeyAdded:@[[NSValue valueWithCGRect:new]],
  299. RITLDifferencesKeyRemoved:@[[NSValue valueWithCGRect:old]]};
  300. }
  301. }
  302. #pragma mark - Dismiss
  303. - (void)dismissPhotoControllers
  304. {
  305. if(self.navigationController.presentingViewController){//如果是模态弹出
  306. [self.navigationController dismissViewControllerAnimated:true completion:nil];
  307. }else if(self.navigationController){
  308. [self.navigationController popViewControllerAnimated:true];
  309. }
  310. }
  311. #pragma mark <UICollectionViewDataSource>
  312. - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
  313. {
  314. return 1;
  315. }
  316. - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
  317. {
  318. return self.assets ? self.assets.count : 0;
  319. }
  320. - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
  321. RITLPhotosCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:reuseIdentifier forIndexPath:indexPath];
  322. //Asset
  323. PHAsset *asset = [self.assets objectAtIndex:indexPath.item];
  324. //Size
  325. CGSize size = [self collectionView:collectionView layout:collectionView.collectionViewLayout sizeForItemAtIndexPath:indexPath];
  326. PHImageRequestOptions *options = PHImageRequestOptions.new;
  327. options.networkAccessAllowed = true;
  328. // Configure the cell
  329. cell.representedAssetIdentifier = asset.localIdentifier;
  330. [self.imageManager requestImageForAsset:asset targetSize:size contentMode:PHImageContentModeAspectFill options:options resultHandler:^(UIImage * _Nullable result, NSDictionary * _Nullable info) {
  331. if ([cell.representedAssetIdentifier isEqualToString:asset.localIdentifier] && result) {
  332. cell.actionTarget = self;
  333. cell.asset = asset;
  334. cell.indexPath = indexPath;
  335. cell.imageView.image = result;
  336. cell.messageView.hidden = (asset.mediaType == PHAssetMediaTypeImage);
  337. if (self.isEdit) {
  338. if (self.oldAsset) {
  339. if ([asset.localIdentifier isEqualToString:self.oldAsset.localIdentifier]) {
  340. BOOL isSelected = [self.dataManager.assetIdentiers containsObject:asset.localIdentifier];
  341. if (isSelected) {
  342. // 当编辑图片返回时 如果是选中状态,则取消选中
  343. [cell.chooseButton sendActionsForControlEvents:UIControlEventTouchUpInside];
  344. }
  345. }
  346. }
  347. //6CAF1A6D-9377-43A4-8D47-87E1CDFF2750/L0/001
  348. if (indexPath.row == self.assets.count -1) {
  349. // 当编辑图片返回时 默认选中,这里是模拟用户点击选择按钮
  350. [cell.chooseButton sendActionsForControlEvents:UIControlEventTouchUpInside];
  351. self.isEdit = NO;
  352. self.isScroll = YES;
  353. }
  354. }
  355. if (@available(iOS 9.1,*)) {//Live图片
  356. cell.liveBadgeImageView.hidden = !(asset.mediaSubtypes == PHAssetMediaSubtypePhotoLive);
  357. }
  358. if(!RITLPhotosConfiguration.defaultConfiguration.containVideo){//是否允许选择视频-不允许选择视频,去掉选择符
  359. cell.chooseButton.hidden = (asset.mediaType == PHAssetMediaTypeVideo);
  360. }
  361. if(!RITLPhotosConfiguration.defaultConfiguration.containImage){//是否允许选择图片-不允许选择图片,去掉选择符
  362. cell.chooseButton.hidden = (asset.mediaType == PHAssetMediaTypeImage);
  363. }
  364. BOOL isSelected = [self.dataManager.assetIdentiers containsObject:asset.localIdentifier];
  365. //进行属性隐藏设置
  366. cell.indexLabel.hidden = !isSelected;
  367. if (isSelected) {
  368. cell.indexLabel.text = @([self.dataManager.assetIdentiers indexOfObject:asset.localIdentifier] + 1).stringValue;
  369. }
  370. if (cell.imageView.hidden) { return; }
  371. cell.messageLabel.text = [NSString timeStringWithTimeDuration:asset.duration];
  372. }
  373. }];
  374. //注册3D Touch
  375. if (@available(iOS 9.0,*)) {
  376. if (self.traitCollection.forceTouchCapability == UIForceTouchCapabilityAvailable){
  377. [self registerForPreviewingWithDelegate:self sourceView:cell];
  378. }
  379. }
  380. return cell;
  381. }
  382. - (void)collectionView:(UICollectionView *)collectionView didEndDisplayingCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath {
  383. // if (self.isScroll && indexPath.row == self.assets.count -1) {
  384. // self.isScroll = NO;
  385. // if (self.collectionView.contentOffset.y < self.collectionView.contentSize.height) {
  386. // // 滚动到底部
  387. // [self.collectionView setContentOffset:CGPointMake(0, self.collectionView.contentSize.height - self.collectionView.frame.size.height) animated:YES];
  388. // }
  389. // }
  390. }
  391. #pragma mark <UIScrollViewDelegate>
  392. - (void)scrollViewDidScroll:(UIScrollView *)scrollView
  393. {
  394. [self updateCachedAssets];
  395. }
  396. #pragma mark <UICollectionViewDelegateFlowLayout>
  397. - (CGSize)collectionView:(UICollectionView *)collectionView
  398. layout:(UICollectionViewLayout*)collectionViewLayout
  399. sizeForItemAtIndexPath:(NSIndexPath *)indexPath
  400. {
  401. CGFloat sizeHeight = (MIN(RITL_SCREEN_WIDTH,RITL_SCREEN_HEIGHT) - 3.0f * 3) / 4;
  402. return CGSizeMake(sizeHeight, sizeHeight);
  403. }
  404. - (CGFloat)collectionView:(UICollectionView *)collectionView
  405. layout:(UICollectionViewLayout*)collectionViewLayout
  406. minimumLineSpacingForSectionAtIndex:(NSInteger)section
  407. {
  408. return 3.f;
  409. }
  410. - (CGFloat)collectionView:(UICollectionView *)collectionView
  411. layout:(UICollectionViewLayout *)collectionViewLayout
  412. minimumInteritemSpacingForSectionAtIndex:(NSInteger)section
  413. {
  414. return 3.f;
  415. }
  416. - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
  417. {
  418. //获取当前的资源
  419. PHAsset *asset = [self.assets objectAtIndex:indexPath.item];
  420. //跳出控制器
  421. [self pushHorAllBrowseViewControllerWithAsset:asset];
  422. }
  423. #pragma mark - <UIViewControllerPreviewingDelegate>
  424. - (nullable UIViewController *)previewingContext:(id <UIViewControllerPreviewing>)previewingContext viewControllerForLocation:(CGPoint)location NS_AVAILABLE_IOS(9_0)
  425. {
  426. //获取当前cell的indexPath
  427. NSIndexPath * indexPath = [self.collectionView indexPathForCell:(RITLPhotosCell *)previewingContext.sourceView];
  428. NSUInteger item = indexPath.item;
  429. //获得当前的资源
  430. PHAsset *asset = self.assets[item];
  431. if (asset.mediaType != PHAssetMediaTypeImage)
  432. {
  433. return nil;
  434. }
  435. RITLPhotosPreviewController * viewController = [RITLPhotosPreviewController previewWithShowAsset:asset];
  436. return viewController;
  437. }
  438. - (void)previewingContext:(id <UIViewControllerPreviewing>)previewingContext commitViewController:(UIViewController *)viewControllerToCommit NS_AVAILABLE_IOS(9_0)
  439. {
  440. //获取当前cell的indexPath
  441. NSIndexPath * indexPath = [self.collectionView indexPathForCell:(RITLPhotosCell *)previewingContext.sourceView];
  442. //获取当前的资源
  443. PHAsset *asset = [self.assets objectAtIndex:indexPath.item];
  444. //跳出控制器
  445. [self pushHorAllBrowseViewControllerWithAsset:asset];
  446. }
  447. #pragma mark - Browse All Assets Display
  448. /// Push 出所有的资源浏览
  449. - (void)pushHorAllBrowseViewControllerWithAsset:(PHAsset *)asset
  450. {
  451. [self.navigationController pushViewController:({
  452. RITLPhotosHorBrowseViewController *browerController = RITLPhotosHorBrowseViewController.new;
  453. RITLPhotosBrowseAllDataSource *dataSource = RITLPhotosBrowseAllDataSource.new;
  454. dataSource.collection = self.assetCollection;
  455. dataSource.asset = asset;
  456. browerController.dataSource = dataSource;
  457. browerController.backHandler = ^(BOOL isEdit, PHAsset *asset) {
  458. self.isEdit = isEdit;
  459. self.oldAsset = asset;
  460. if (!self.isEdit) {
  461. [self.collectionView reloadData];
  462. }
  463. [self editAfterReloadCollectionView];
  464. };
  465. browerController;
  466. }) animated:true];
  467. }
  468. /// Push出已经选择的资源浏览
  469. - (void)pushPreviewViewController
  470. {
  471. [self.navigationController pushViewController:({
  472. RITLPhotosHorBrowseViewController *browerController = RITLPhotosHorBrowseViewController.new;
  473. RITLPhotosBrowseDataSource *dataSource = RITLPhotosBrowseDataSource.new;
  474. dataSource.assets = self.dataManager.assets;
  475. browerController.dataSource = dataSource;
  476. browerController.backHandler = ^(BOOL isEdit, PHAsset *asset) {
  477. self.isEdit = isEdit;
  478. self.oldAsset = asset;
  479. if (!self.isEdit) {
  480. [self.collectionView reloadData];
  481. }
  482. [self editAfterReloadCollectionView];
  483. };
  484. browerController;
  485. }) animated:true];
  486. }
  487. - (void)editAfterReloadCollectionView {
  488. if (self.isEdit) {
  489. self.imageManager = [PHCachingImageManager new];
  490. self.photoLibrary = PHPhotoLibrary.sharedPhotoLibrary;
  491. [self resetCachedAssets];
  492. self.assets = [PHAsset fetchAssetsInAssetCollection:self.assetCollection options:nil];
  493. [self.collectionView reloadData];
  494. if (self.collectionView.contentOffset.y < self.collectionView.contentSize.height) {
  495. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(.5f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  496. // 滚动到底部
  497. [self.collectionView setContentOffset:CGPointMake(0, self.collectionView.contentSize.height - self.collectionView.frame.size.height) animated:NO];
  498. });
  499. }
  500. }
  501. }
  502. #pragma mark - Send
  503. - (void)pushPhotosMaker {
  504. [RITLPhotosMaker.sharedInstance startMakePhotosComplete:^{
  505. [self dismissPhotoControllers];
  506. }];
  507. }
  508. #pragma mark - collectionView
  509. -(UICollectionView *)collectionView
  510. {
  511. if(_collectionView == nil)
  512. {
  513. _collectionView = [[UICollectionView alloc]initWithFrame:CGRectZero collectionViewLayout:[[UICollectionViewFlowLayout alloc]init]];
  514. //protocol
  515. _collectionView.delegate = self;
  516. _collectionView.dataSource = self;
  517. //property
  518. _collectionView.backgroundColor = [UIColor whiteColor];
  519. }
  520. return _collectionView;
  521. }
  522. - (UILabel *)tintLable {
  523. if (!_tintLable) {
  524. _tintLable = [[UILabel alloc] init];
  525. _tintLable.font = SYSFONT(16);
  526. _tintLable.text = [NSString stringWithFormat:Localized(@"JX_NotGalleryPermissions"),APP_NAME];
  527. _tintLable.textAlignment = NSTextAlignmentCenter;
  528. _tintLable.numberOfLines = 0;
  529. _tintLable.hidden = YES;
  530. }
  531. return _tintLable;
  532. }
  533. #pragma mark - <RITLPhotosCellActionTarget>
  534. - (void)photosCellDidTouchUpInSlide:(RITLPhotosCell *)cell asset:(PHAsset *)asset indexPath:(NSIndexPath *)indexPath complete:(RITLPhotosCellStatusAction)animated
  535. {
  536. if (self.dataManager.count >= RITLPhotosConfiguration.defaultConfiguration.maxCount &&
  537. ![self.dataManager containAsset:asset]/*是添加*/) {
  538. [g_server showMsg:Localized(@"JX_CannotSelectMorePhotos")];
  539. return;
  540. }//不能进行选择
  541. NSInteger index = [self.dataManager addOrRemoveAsset:asset].integerValue;
  542. animated(RITLPhotosCellAnimatedStatusPermit,index > 0,MAX(0,index));
  543. if (index < 0 && !self.isEdit) {//表示进行了取消操作
  544. [self.collectionView reloadData];
  545. }
  546. }
  547. #pragma mark - <KVO>
  548. - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
  549. {
  550. if ([keyPath isEqualToString:@"count"] && [object isEqual:self.dataManager]) {
  551. [self changedBottomViewStatus];
  552. }
  553. else if([keyPath isEqualToString:@"hightQuality"] && [object isEqual:self.dataManager]){
  554. BOOL hightQuality = [change[NSKeyValueChangeNewKey] boolValue];
  555. self.bottomView.fullImageButton.selected = hightQuality;
  556. }
  557. }
  558. #pragma mark - 检测
  559. /// 检测底部视图的状态
  560. - (void)changedBottomViewStatus
  561. {
  562. NSInteger count = self.dataManager.count;
  563. self.bottomView.previewButton.enabled = !(count == 0);
  564. UIControlState state = (count == 0 ? UIControlStateDisabled : UIControlStateNormal);
  565. NSString *title;
  566. if(!RITLPhotosConfiguration.defaultConfiguration.isRichScan){
  567. title = (count == 0 ? Localized(@"JX_Send") : [NSString stringWithFormat:@"%@(%@)",Localized(@"JX_Send"),@(count)]);
  568. }else {
  569. title = Localized(@"JX_Finish");
  570. }
  571. [self.bottomView.sendButton setTitle:title forState:state];
  572. self.bottomView.sendButton.enabled = !(count == 0);
  573. }
  574. #pragma mark - action
  575. - (void)hightQualityShouldChanged:(UIButton *)sender
  576. {
  577. self.dataManager.hightQuality = !self.dataManager.hightQuality;
  578. }
  579. @end