NSArray+HXExtension.m 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. //
  2. // NSArray+HXExtension.m
  3. // HXPhotoPickerExample
  4. //
  5. // Created by Silence on 2019/1/7.
  6. // Copyright © 2019年 Silence. All rights reserved.
  7. //
  8. #import "NSArray+HXExtension.h"
  9. #import "HXPhotoModel.h"
  10. #import "HXPhotoEdit.h"
  11. #import "HXPhotoManager.h"
  12. @implementation NSArray (HXExtension)
  13. - (BOOL)hx_detection {
  14. if (!self.count) {
  15. return NO;
  16. }
  17. for (id obj in self) {
  18. if (![obj respondsToSelector:@selector(isKindOfClass:)]) {
  19. return NO;
  20. }
  21. if (![obj isKindOfClass:[HXPhotoModel class]]) {
  22. return NO;
  23. }
  24. }
  25. return YES;
  26. }
  27. + (NSArray *)hx_dictHandler:(NSDictionary *)dict {
  28. NSMutableArray *dataArray = [NSMutableArray array];
  29. NSArray *keys = [dict.allKeys sortedArrayUsingComparator:^NSComparisonResult(NSString * obj1, NSString * obj2) {
  30. if (obj1.integerValue > obj2.integerValue) {
  31. return NSOrderedDescending;
  32. }else if (obj1.integerValue < obj2.integerValue) {
  33. return NSOrderedAscending;
  34. }else {
  35. return NSOrderedSame;
  36. }
  37. }];
  38. for (NSString *key in keys) {
  39. if ([dict[key] isKindOfClass:[NSError class]]) {
  40. continue;
  41. }
  42. if ([dict[key] isKindOfClass:[NSString class]]) {
  43. NSString *path = dict[key];
  44. UIImage *image = [self hx_disposeHEICWithPath:path];
  45. if (image) {
  46. if (image.imageOrientation != UIImageOrientationUp) {
  47. image = [image hx_normalizedImage];
  48. }
  49. [dataArray addObject:image];
  50. }
  51. }else {
  52. [dataArray addObject:dict[key]];
  53. }
  54. }
  55. return dataArray.copy;
  56. }
  57. + (UIImage *)hx_disposeHEICWithPath:(NSString *)path {
  58. if ([path.pathExtension isEqualToString:@"HEIC"]) {
  59. // 处理一下 HEIC 格式图片
  60. CIImage *ciImage = [CIImage imageWithContentsOfURL:[NSURL fileURLWithPath:path]];
  61. CIContext *context = [CIContext context];
  62. NSString *key = (__bridge NSString *)kCGImageDestinationLossyCompressionQuality;
  63. NSData *jpgData = [context JPEGRepresentationOfImage:ciImage colorSpace:ciImage.colorSpace options:@{key : @1}];
  64. UIImage *image = [UIImage imageWithData:jpgData];
  65. if (image.imageOrientation != UIImageOrientationUp) {
  66. image = [image hx_normalizedImage];
  67. }
  68. return image;
  69. }else {
  70. NSData *imageData = [NSData dataWithContentsOfFile:path];
  71. UIImage *image = [UIImage imageWithData:imageData];
  72. if (!image) {
  73. if (image.imageOrientation != UIImageOrientationUp) {
  74. image = [image hx_normalizedImage];
  75. }
  76. image = [UIImage imageWithContentsOfFile:path];
  77. }
  78. return image;
  79. }
  80. }
  81. - (void)hx_requestImageWithOriginal:(BOOL)original completion:(nonnull void (^)(NSArray<UIImage *> * _Nullable, NSArray<HXPhotoModel *> * _Nullable))completion {
  82. if (![self hx_detection] || !self.count) {
  83. if (completion) {
  84. completion(nil, self);
  85. }
  86. if (HXShowLog) NSSLog(@"数组里装的不是HXPhotoModel对象或者为空");
  87. return;
  88. }
  89. NSInteger count = self.count;
  90. NSMutableDictionary *dict = [NSMutableDictionary dictionary];
  91. for (int i = 0 ; i < self.count; i++) {
  92. HXPhotoModel *model = self[i];
  93. [dict setValue:[NSError errorWithDomain:@"获取失败" code:99999 userInfo:nil] forKey:model.selectIndexStr];
  94. }
  95. __block NSInteger index = 0;
  96. NSMutableArray *errorArray = [NSMutableArray array];
  97. for (HXPhotoModel *model in self) {
  98. [self requestImageWithOriginal:original photoModel:model successful:^(UIImage * _Nullable image, NSURL * _Nullable imagePath, HXPhotoModel *photoModel) {
  99. if (image) {
  100. // 如果photoModel 为nil可能是数组里的模型被移除了
  101. [dict setObject:image forKey:photoModel.selectIndexStr];
  102. }else if (imagePath) {
  103. UIImage *hImage = [NSArray hx_disposeHEICWithPath:imagePath.relativePath];
  104. if (hImage) {
  105. photoModel.thumbPhoto = hImage;
  106. photoModel.previewPhoto = hImage;
  107. [dict setObject:hImage forKey:photoModel.selectIndexStr];
  108. }else {
  109. [errorArray addObject:photoModel];
  110. }
  111. }else {
  112. [errorArray addObject:photoModel];
  113. }
  114. index++;
  115. if (index == count) {
  116. if (completion) {
  117. completion([NSArray hx_dictHandler:dict], errorArray);
  118. }
  119. }
  120. } failure:^(HXPhotoModel *photoModel) {
  121. [errorArray addObject:photoModel];
  122. index++;
  123. if (index == count) {
  124. if (completion) {
  125. completion([NSArray hx_dictHandler:dict], errorArray);
  126. }
  127. }
  128. }];
  129. }
  130. }
  131. - (void)hx_requestImageSeparatelyWithOriginal:(BOOL)original completion:(void (^)(NSArray<UIImage *> * _Nullable, NSArray<HXPhotoModel *> * _Nullable))completion {
  132. if (![self hx_detection] || !self.count) {
  133. if (completion) {
  134. completion(nil, self);
  135. }
  136. if (HXShowLog) NSSLog(@"数组里装的不是HXPhotoModel对象或者为空");
  137. return;
  138. }
  139. [self requestImageSeparatelyWithOriginal:original imageList:@[].mutableCopy photoModels:self.mutableCopy errorPhotoModels:@[].mutableCopy completion:completion];
  140. }
  141. - (void)requestImageSeparatelyWithOriginal:(BOOL)original
  142. imageList:(NSMutableArray *)imageList
  143. photoModels:(NSMutableArray *)photoModels
  144. errorPhotoModels:(NSMutableArray *)errorPhotoModels
  145. completion:(void (^)(NSArray<UIImage *> * _Nullable imageArray, NSArray<HXPhotoModel *> * _Nullable errorArray))completion {
  146. if (!photoModels.count) {
  147. if (completion) {
  148. completion(imageList, errorPhotoModels);
  149. }
  150. return;
  151. }
  152. if (!imageList) imageList = [NSMutableArray array];
  153. if (!errorPhotoModels) errorPhotoModels = [NSMutableArray array];
  154. HXPhotoModel *model = photoModels.firstObject;
  155. HXWeakSelf
  156. [self requestImageWithOriginal:original photoModel:model successful:^(UIImage * _Nullable image, NSURL * _Nullable imagePath, HXPhotoModel *photoModel) {
  157. if (image) {
  158. photoModel.thumbPhoto = image;
  159. photoModel.previewPhoto = image;
  160. [imageList addObject:image];
  161. }else if (imagePath) {
  162. UIImage *hImage = [NSArray hx_disposeHEICWithPath:imagePath.relativePath];
  163. if (hImage) {
  164. photoModel.thumbPhoto = hImage;
  165. photoModel.previewPhoto = hImage;
  166. [imageList addObject:hImage];
  167. }else {
  168. //已知在iPhone 8,iOS 10.0.2系统上,通过requestImageDataStartRequestICloud能获取到图片的URL,但通过此URL并不能获取到image。故调用requestPreviewImageWithSize方法获取image,并存到沙盒tmp下
  169. [model requestPreviewImageWithSize:PHImageManagerMaximumSize startRequestICloud:^(PHImageRequestID iCloudRequestId, HXPhotoModel * _Nullable model) {
  170. } progressHandler:^(double progress, HXPhotoModel * _Nullable model) {
  171. } success:^(UIImage * _Nullable imageValue, HXPhotoModel * _Nullable model, NSDictionary * _Nullable info) {
  172. NSString *photoPathStr = [NSTemporaryDirectory() stringByAppendingString:@"HXPhotoPickerSave/"];
  173. BOOL isDir;
  174. BOOL isDirExit = [[NSFileManager defaultManager] fileExistsAtPath:photoPathStr isDirectory:&isDir];
  175. NSError *error;
  176. if (isDirExit == NO) {
  177. [[NSFileManager defaultManager] createDirectoryAtPath:photoPathStr withIntermediateDirectories:YES attributes:nil error:&error];
  178. }
  179. if (error) {
  180. [errorPhotoModels addObject:photoModel];
  181. }
  182. else {
  183. NSInteger timeStamp = [[NSDate new] timeIntervalSince1970];
  184. NSString *imgPath = [NSString stringWithFormat:@"%@%zd_%zd.jpg",photoPathStr, timeStamp, photoModels.count];
  185. [UIImageJPEGRepresentation(imageValue, 1.0) writeToFile:imgPath atomically:YES];
  186. photoModel.imageURL = [NSURL fileURLWithPath:imgPath];
  187. photoModel.thumbPhoto = imageValue;
  188. photoModel.previewPhoto = imageValue;
  189. [imageList addObject:imageValue];
  190. }
  191. [photoModels removeObjectAtIndex:0];
  192. if (!photoModels.count) {
  193. [weakSelf requestImageSeparatelyWithOriginal:original imageList:imageList.copy photoModels:photoModels errorPhotoModels:errorPhotoModels.copy completion:completion];
  194. }else {
  195. [weakSelf requestImageSeparatelyWithOriginal:original imageList:imageList photoModels:photoModels errorPhotoModels:errorPhotoModels completion:completion];
  196. }
  197. } failed:^(NSDictionary * _Nullable info, HXPhotoModel * _Nullable model) {
  198. [errorPhotoModels addObject:photoModel];
  199. [photoModels removeObjectAtIndex:0];
  200. if (!photoModels.count) {
  201. [weakSelf requestImageSeparatelyWithOriginal:original imageList:imageList.copy photoModels:photoModels errorPhotoModels:errorPhotoModels.copy completion:completion];
  202. }else {
  203. [weakSelf requestImageSeparatelyWithOriginal:original imageList:imageList photoModels:photoModels errorPhotoModels:errorPhotoModels completion:completion];
  204. }
  205. }];
  206. return;
  207. }
  208. }else {
  209. [errorPhotoModels addObject:photoModel];
  210. }
  211. [photoModels removeObjectAtIndex:0];
  212. if (!photoModels.count) {
  213. [weakSelf requestImageSeparatelyWithOriginal:original imageList:imageList.copy photoModels:photoModels errorPhotoModels:errorPhotoModels.copy completion:completion];
  214. }else {
  215. [weakSelf requestImageSeparatelyWithOriginal:original imageList:imageList photoModels:photoModels errorPhotoModels:errorPhotoModels completion:completion];
  216. }
  217. } failure:^(HXPhotoModel *photoModel) {
  218. [errorPhotoModels addObject:photoModel];
  219. [photoModels removeObjectAtIndex:0];
  220. if (!photoModels.count) {
  221. [weakSelf requestImageSeparatelyWithOriginal:original imageList:imageList.copy photoModels:photoModels errorPhotoModels:errorPhotoModels.copy completion:completion];
  222. }else {
  223. [weakSelf requestImageSeparatelyWithOriginal:original imageList:imageList photoModels:photoModels errorPhotoModels:errorPhotoModels completion:completion];
  224. }
  225. }];
  226. }
  227. - (void)requestImageWithOriginal:(BOOL)original photoModel:(HXPhotoModel *)photoModel successful:(void (^)(UIImage * _Nullable image, NSURL * _Nullable imagePath, HXPhotoModel *photoModel))successful failure:(void (^)(HXPhotoModel *photoModel))failure {
  228. if (photoModel.photoEdit) {
  229. UIImage *image = photoModel.photoEdit.editPreviewImage;
  230. if (successful) {
  231. successful(image, nil, photoModel);
  232. }
  233. return;
  234. }
  235. if (photoModel.type == HXPhotoModelMediaTypeCameraPhoto) {
  236. if (photoModel.networkPhotoUrl) {
  237. if ([HXPhotoCommon photoCommon].requestNetworkAfter) {
  238. // 需要下载网络图片就将 [HXPhotoCommon photoCommon].requestNetworkAfter = YES
  239. [HXPhotoModel requestImageWithURL:photoModel.networkPhotoUrl progress:nil completion:^(UIImage * _Nullable image, NSURL * _Nullable url, NSError * _Nullable error) {
  240. if (image) {
  241. photoModel.thumbPhoto = image;
  242. photoModel.previewPhoto = image;
  243. if (successful) {
  244. successful(image, nil, photoModel);
  245. }
  246. }else {
  247. if (failure) {
  248. failure(photoModel);
  249. }
  250. if (HXShowLog) NSSLog(@"网络图片获取失败!");
  251. }
  252. }];
  253. }else {
  254. if (successful) {
  255. successful(photoModel.thumbPhoto, nil, photoModel);
  256. }
  257. }
  258. }else {
  259. // 本地图片
  260. if (successful) {
  261. successful(photoModel.thumbPhoto, nil, photoModel);
  262. }
  263. }
  264. return;
  265. }else {
  266. if (!photoModel.asset) {
  267. if (photoModel.thumbPhoto) {
  268. if (successful) {
  269. successful(photoModel.thumbPhoto, nil, photoModel);
  270. }
  271. }else {
  272. if (failure) {
  273. failure(photoModel);
  274. }
  275. }
  276. return;
  277. }
  278. }
  279. if ((original && photoModel.type != HXPhotoModelMediaTypeVideo) ||
  280. photoModel.type == HXPhotoModelMediaTypePhotoGif) {
  281. // 如果选择了原图,就换一种获取方式
  282. [photoModel getAssetURLWithSuccess:^(NSURL * _Nullable imageURL, HXPhotoModelMediaSubType mediaType, BOOL isNetwork, HXPhotoModel * _Nullable model) {
  283. if (successful) {
  284. successful(nil, imageURL, model);
  285. }
  286. } failed:^(NSDictionary * _Nullable info, HXPhotoModel * _Nullable model) {
  287. if (failure) {
  288. failure(model);
  289. }
  290. }];
  291. }else {
  292. [photoModel requestImageDataStartRequestICloud:nil progressHandler:nil success:^(NSData * _Nullable imageData, UIImageOrientation orientation, HXPhotoModel * _Nullable model, NSDictionary * _Nullable info) {
  293. UIImage *image = [UIImage imageWithData:imageData];
  294. if (image.imageOrientation != UIImageOrientationUp) {
  295. image = [image hx_normalizedImage];
  296. }
  297. // 不是原图那就压缩
  298. if (!original) {
  299. image = [image hx_scaleImagetoScale:0.5f];
  300. }
  301. if (successful) {
  302. successful(image, nil, model);
  303. }
  304. } failed:^(NSDictionary * _Nullable info, HXPhotoModel * _Nullable model) {
  305. if (model.previewPhoto) {
  306. if (successful) {
  307. successful(model.previewPhoto, nil, model);
  308. }
  309. }else {
  310. if (failure) {
  311. failure(model);
  312. }
  313. }
  314. }];
  315. }
  316. }
  317. - (void)hx_requestImageDataWithCompletion:(void (^)(NSArray<NSData *> * _Nullable imageDataArray))completion {
  318. if (![self hx_detection] || !self.count) {
  319. if (completion) {
  320. completion(nil);
  321. }
  322. if (HXShowLog) NSSLog(@"数组里装的不是HXPhotoModel对象或者为空");
  323. return;
  324. }
  325. NSMutableDictionary *dict = [NSMutableDictionary dictionary];
  326. for (int i = 0 ; i < self.count; i++) {
  327. HXPhotoModel *model = self[i];
  328. [dict setValue:[NSError errorWithDomain:@"获取失败" code:99999 userInfo:nil] forKey:model.selectIndexStr];
  329. }
  330. __block NSInteger index = 0;
  331. NSInteger count = self.count;
  332. for (HXPhotoModel *model in self) {
  333. if (model.subType != HXPhotoModelMediaSubTypePhoto) {
  334. continue;
  335. }
  336. [model requestImageDataStartRequestICloud:nil progressHandler:nil success:^(NSData *imageData, UIImageOrientation orientation, HXPhotoModel *model, NSDictionary *info) {
  337. if (model.asset && [HXPhotoTools assetIsHEIF:model.asset]) {
  338. CIImage *ciImage = [CIImage imageWithData:imageData];
  339. CIContext *context = [CIContext context];
  340. NSData *jpgData = [context JPEGRepresentationOfImage:ciImage colorSpace:ciImage.colorSpace options:@{}];
  341. [dict setObject:jpgData forKey:model.selectIndexStr];
  342. }else {
  343. [dict setObject:imageData forKey:model.selectIndexStr];
  344. }
  345. index++;
  346. if (index == count) {
  347. if (completion) {
  348. completion([NSArray hx_dictHandler:dict]);
  349. }
  350. }
  351. } failed:^(NSDictionary *info, HXPhotoModel *model) {
  352. index++;
  353. if (HXShowLog) NSSLog(@"一个获取失败!");
  354. if (index == count) {
  355. if (completion) {
  356. completion([NSArray hx_dictHandler:dict]);
  357. }
  358. }
  359. }];
  360. }
  361. }
  362. @end