DNImagePickerHelper.m 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. //
  2. // DNImagePickerHelper.m
  3. // DNImagePicker
  4. //
  5. // Created by Ding Xiao on 16/8/23.
  6. // Copyright © 2016年 Dennis. All rights reserved.
  7. //
  8. #import <Photos/Photos.h>
  9. #import "DNImagePickerHelper.h"
  10. #import "DNImageFetchOperation.h"
  11. #import "DNAlbum.h"
  12. #import "DNAsset.h"
  13. NSString * const DNImagePickerPhotoLibraryChangedNotification = @"DNImagePickerPhotoLibraryChangedNotification";
  14. static NSString* const kDNImagePickerStoredGroupKey = @"com.dennis.kDNImagePickerStoredGroup";
  15. static dispatch_queue_t imageFetchQueue() {
  16. static dispatch_queue_t queue = nil;
  17. static dispatch_once_t onceToken;
  18. dispatch_once(&onceToken, ^{
  19. queue = dispatch_queue_create("com.awesomedennis.dnimageFetchQueue", DISPATCH_QUEUE_SERIAL);
  20. });
  21. return queue;
  22. }
  23. @interface DNImagePickerHelper () <PHPhotoLibraryChangeObserver>
  24. @property (nonatomic, strong) NSOperationQueue *imageFetchQueue;
  25. @property (nonatomic, strong) NSMutableDictionary<NSString *, DNImageFetchOperation*> *fetchImageOperationDics;
  26. @end
  27. @implementation DNImagePickerHelper
  28. + (instancetype)sharedHelper {
  29. static dispatch_once_t once;
  30. static id instance;
  31. dispatch_once(&once, ^{
  32. instance = [self new];
  33. });
  34. return instance;
  35. }
  36. - (instancetype)init {
  37. self = [super init];
  38. if (self) {
  39. _imageFetchQueue = [NSOperationQueue new];
  40. _imageFetchQueue.maxConcurrentOperationCount = 8;
  41. _imageFetchQueue.name = @"com.awesomedennis.dnnimagefetchOperationQueue";
  42. _fetchImageOperationDics = [NSMutableDictionary dictionary];
  43. }
  44. return self;
  45. }
  46. #pragma mark -
  47. #pragma mark - PHPhotoLibraryChangeObserver
  48. - (void)photoLibraryDidChange:(PHChange *)changeInstance {
  49. dispatch_async(dispatch_get_main_queue(), ^{
  50. [[NSNotificationCenter defaultCenter] postNotificationName:DNImagePickerPhotoLibraryChangedNotification object:nil];
  51. });
  52. }
  53. #pragma mark -
  54. #pragma mark - public
  55. + (DNAlbumAuthorizationStatus)authorizationStatus {
  56. return (DNAlbumAuthorizationStatus)[PHPhotoLibrary authorizationStatus];
  57. }
  58. + (void)requestAlbumListWithCompleteHandler:(void (^)(NSArray<DNAlbum *> *))completeHandelr {
  59. dispatch_block_t block = ^{
  60. NSMutableArray *albums = [NSMutableArray arrayWithArray:[self fetchAlbumsResults]];
  61. if (!albums) {
  62. completeHandelr(nil);
  63. return;
  64. }
  65. PHFetchOptions *userAlbumsOptions = [[PHFetchOptions alloc] init];
  66. userAlbumsOptions.predicate = [NSPredicate predicateWithFormat:@"mediaType = %@ or mediaType = %@",@(PHAssetMediaTypeImage), @(PHAssetMediaTypeVideo)];
  67. userAlbumsOptions.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:YES]];
  68. NSMutableArray *list = [NSMutableArray array];
  69. for (PHFetchResult *result in albums) {
  70. [result enumerateObjectsUsingBlock:^(PHAssetCollection *obj, NSUInteger idx, BOOL * _Nonnull stop) {
  71. PHFetchResult *assetResults = [PHAsset fetchAssetsInAssetCollection:obj options:userAlbumsOptions];
  72. NSInteger count = 0;
  73. switch (obj.assetCollectionType) {
  74. case PHAssetCollectionTypeAlbum:
  75. case PHAssetCollectionTypeSmartAlbum:
  76. count = assetResults.count;
  77. break;
  78. default:
  79. count = 0;
  80. break;
  81. }
  82. if (count > 0) {
  83. @autoreleasepool {
  84. DNAlbum *album = [DNAlbum albumWithAssetCollection:obj results:assetResults];
  85. [list addObject:album];
  86. }
  87. }
  88. }];
  89. }
  90. dispatch_async(dispatch_get_main_queue(), ^{
  91. if (completeHandelr) {
  92. completeHandelr([list copy]);
  93. }
  94. });
  95. };
  96. dispatch_async(imageFetchQueue(), block);
  97. }
  98. + (void)requestCurrentAblumWithCompleteHandler:(void (^)(DNAlbum *))completeHandler {
  99. void(^callBack)(DNAlbum *) = ^(DNAlbum * album){
  100. dispatch_async(dispatch_get_main_queue(), ^{
  101. completeHandler(album);
  102. });
  103. };
  104. dispatch_async(imageFetchQueue(), ^{
  105. DNAlbum *album = [[DNAlbum alloc] init];
  106. NSString *identifier = [DNImagePickerHelper albumIdentifier];
  107. if (!identifier || identifier.length <= 0) {
  108. callBack(album);
  109. return;
  110. }
  111. PHFetchResult *result = [PHAssetCollection fetchAssetCollectionsWithLocalIdentifiers:@[identifier] options:nil];
  112. if (result.count <= 0) {
  113. callBack(album);
  114. return;
  115. }
  116. PHFetchOptions *options = [[PHFetchOptions alloc] init];
  117. options.predicate = [NSPredicate predicateWithFormat:@"mediaType = %@ or mediaType = %@",@(PHAssetMediaTypeImage), @(PHAssetMediaTypeVideo)];
  118. options.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:YES]];
  119. PHAssetCollection *collection = result.firstObject;
  120. PHFetchResult *requestReslut = [PHAsset fetchAssetsInAssetCollection:collection options:options];
  121. album.albumTitle = collection.localizedTitle;
  122. album.results = requestReslut;
  123. album.count = requestReslut.count;
  124. album.identifier = collection.localIdentifier;
  125. callBack(album);
  126. });
  127. }
  128. + (void)fetchImageAssetsInAlbum:(DNAlbum *)album completeHandler:(void (^)(NSArray<DNAsset *> *))completeHandler {
  129. dispatch_async(imageFetchQueue(), ^{
  130. NSArray<DNAsset *> *array = [self fetchImageAssetsViaCollectionResults:album.results];
  131. dispatch_async(dispatch_get_main_queue(), ^{
  132. if (completeHandler) {
  133. completeHandler(array);
  134. }
  135. });
  136. });
  137. }
  138. + (void)fetchImageSizeWithAsset:(DNAsset *)asset
  139. imageSizeResultHandler:(void (^)(CGFloat imageSize, NSString * sizeString))handler {
  140. if (!asset.asset) {
  141. handler(0,@"0M");
  142. return;
  143. }
  144. [[PHImageManager defaultManager] requestImageDataForAsset:asset.asset
  145. options:nil
  146. resultHandler:^(NSData *imageData,
  147. NSString *dataUTI,
  148. UIImageOrientation orientation,
  149. NSDictionary *info) {
  150. NSString *string = @"0M";
  151. CGFloat imageSize = 0.0;
  152. if (!imageData) {
  153. handler(imageSize, string);
  154. return;
  155. }
  156. imageSize = imageData.length;
  157. if (imageSize > 1024*1024) {
  158. CGFloat size = imageSize/(1024*2024);
  159. string = [NSString stringWithFormat:@"%.1fM",size];
  160. } else {
  161. CGFloat size = imageSize/1024;
  162. string = [NSString stringWithFormat:@"%.1fK",size];
  163. }
  164. handler(imageSize, string);
  165. }];
  166. }
  167. + (void)fetchImageWithAsset:(DNAsset *)asset
  168. targetSize:(CGSize)targetSize
  169. imageResutHandler:(void (^)(UIImage *))handler {
  170. return [self fetchImageWithAsset:asset targetSize:targetSize needHighQuality:NO imageResutHandler:handler];
  171. }
  172. + (void)fetchImageWithAsset:(DNAsset *)asset
  173. targetSize:(CGSize)targetSize
  174. needHighQuality:(BOOL)isHighQuality
  175. imageResutHandler:(void (^)(UIImage *image))handler {
  176. if (!asset) {
  177. return;
  178. }
  179. DNImagePickerHelper *helper = [DNImagePickerHelper sharedHelper];
  180. DNImageFetchOperation *operation = [[DNImageFetchOperation alloc] initWithAsset:asset.asset];
  181. __weak typeof(helper) whelper = helper;
  182. [operation fetchImageWithTargetSize:targetSize needHighQuality:isHighQuality imageResutHandler:^(UIImage * _Nonnull image) {
  183. __strong typeof(whelper) shelper = whelper;
  184. [shelper.fetchImageOperationDics removeObjectForKey:asset.assetIdentifier];
  185. handler(image);
  186. }];
  187. [helper.imageFetchQueue addOperation:operation];
  188. [helper.fetchImageOperationDics setObject:operation forKey:asset.assetIdentifier];
  189. }
  190. + (void)cancelFetchWithAssets:(DNAsset *)asset {
  191. if (!asset) {
  192. return;
  193. }
  194. DNImagePickerHelper *helper = [DNImagePickerHelper sharedHelper];
  195. DNImageFetchOperation *operation = [helper.fetchImageOperationDics objectForKey:asset.assetIdentifier];
  196. if (operation) {
  197. [operation cancel];
  198. }
  199. [helper.fetchImageOperationDics removeObjectForKey:asset.assetIdentifier];
  200. }
  201. #pragma mark -
  202. #pragma mark - priviate
  203. /**
  204. * fetch `PHAsset` array via CollectionResults
  205. *
  206. * @param results collection fetch results
  207. *
  208. * @return `DNAsset` array in collection
  209. */
  210. + (NSArray *)fetchImageAssetsViaCollectionResults:(PHFetchResult *)results {
  211. NSMutableArray *array = [NSMutableArray arrayWithCapacity:results.count];
  212. if (!results) {
  213. return array;
  214. }
  215. [results enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
  216. @autoreleasepool {
  217. DNAsset *asset = [DNAsset assetWithPHAsset:obj];
  218. [array addObject:asset];
  219. }
  220. }];
  221. return [array copy];
  222. }
  223. + (NSArray *)fetchAlbumsResults {
  224. PHFetchOptions *userAlbumsOptions = [[PHFetchOptions alloc] init];
  225. userAlbumsOptions.predicate = [NSPredicate predicateWithFormat:@"estimatedAssetCount > 0"];
  226. userAlbumsOptions.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"startDate" ascending:YES]];
  227. NSMutableArray *albumsArray = [NSMutableArray array];
  228. [albumsArray addObject:
  229. [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum
  230. subtype:PHAssetCollectionSubtypeAlbumRegular
  231. options:nil]];
  232. [albumsArray addObject:
  233. [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum
  234. subtype:PHAssetCollectionSubtypeAny
  235. options:userAlbumsOptions]];
  236. return albumsArray;
  237. }
  238. + (void)saveAblumIdentifier:(NSString *)identifier {
  239. if (identifier.length <= 0) return;
  240. [[NSUserDefaults standardUserDefaults] setObject:identifier forKey:kDNImagePickerStoredGroupKey];
  241. [[NSUserDefaults standardUserDefaults] synchronize];
  242. }
  243. + (NSString *)albumIdentifier {
  244. return [[NSUserDefaults standardUserDefaults] objectForKey:kDNImagePickerStoredGroupKey];
  245. }
  246. @end