TZImageManager.m 45 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940
  1. //
  2. // TZImageManager.m
  3. // TZImagePickerController
  4. //
  5. // Created by 谭真 on 16/1/4.
  6. // Copyright © 2016年 谭真. All rights reserved.
  7. //
  8. #import "TZImageManager.h"
  9. #import <AssetsLibrary/AssetsLibrary.h>
  10. #import "TZAssetModel.h"
  11. #import "TZImagePickerController.h"
  12. @interface TZImageManager ()
  13. #pragma clang diagnostic push
  14. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  15. @property (nonatomic, strong) ALAssetsLibrary *assetLibrary;
  16. @end
  17. @implementation TZImageManager
  18. static CGSize AssetGridThumbnailSize;
  19. static CGFloat TZScreenWidth;
  20. static CGFloat TZScreenScale;
  21. + (instancetype)manager {
  22. static TZImageManager *manager;
  23. static dispatch_once_t onceToken;
  24. dispatch_once(&onceToken, ^{
  25. manager = [[self alloc] init];
  26. if (iOS8Later) {
  27. manager.cachingImageManager = [[PHCachingImageManager alloc] init];
  28. // manager.cachingImageManager.allowsCachingHighQualityImages = YES;
  29. }
  30. TZScreenWidth = [UIScreen mainScreen].bounds.size.width;
  31. // 测试发现,如果scale在plus真机上取到3.0,内存会增大特别多。故这里写死成2.0
  32. TZScreenScale = 2.0;
  33. if (TZScreenWidth > 700) {
  34. TZScreenScale = 1.5;
  35. }
  36. });
  37. return manager;
  38. }
  39. - (void)setColumnNumber:(NSInteger)columnNumber {
  40. _columnNumber = columnNumber;
  41. CGFloat margin = 4;
  42. CGFloat itemWH = (TZScreenWidth - 2 * margin - 4) / columnNumber - margin;
  43. AssetGridThumbnailSize = CGSizeMake(itemWH * TZScreenScale, itemWH * TZScreenScale);
  44. }
  45. - (ALAssetsLibrary *)assetLibrary {
  46. if (_assetLibrary == nil) _assetLibrary = [[ALAssetsLibrary alloc] init];
  47. return _assetLibrary;
  48. }
  49. /// Return YES if Authorized 返回YES如果得到了授权
  50. - (BOOL)authorizationStatusAuthorized {
  51. NSInteger status = [self authorizationStatus];
  52. if (status == 0) {
  53. /**
  54. * 当某些情况下AuthorizationStatus == AuthorizationStatusNotDetermined时,无法弹出系统首次使用的授权alertView,系统应用设置里亦没有相册的设置,此时将无法使用,故作以下操作,弹出系统首次使用的授权alertView
  55. */
  56. [self requestAuthorizationWhenNotDetermined];
  57. }
  58. return status == 3;
  59. }
  60. - (NSInteger)authorizationStatus {
  61. if (iOS8Later) {
  62. return [PHPhotoLibrary authorizationStatus];
  63. } else {
  64. return [ALAssetsLibrary authorizationStatus];
  65. }
  66. return NO;
  67. }
  68. //AuthorizationStatus == AuthorizationStatusNotDetermined 时询问授权弹出系统授权alertView
  69. - (void)requestAuthorizationWhenNotDetermined {
  70. if (iOS8Later) {
  71. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  72. [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
  73. dispatch_async(dispatch_get_main_queue(), ^{
  74. });
  75. }];
  76. });
  77. } else {
  78. [self.assetLibrary enumerateGroupsWithTypes:ALAssetsGroupAll usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
  79. } failureBlock:nil];
  80. }
  81. }
  82. #pragma mark - Get Album
  83. /// Get Album 获得相册/相册数组
  84. - (void)getCameraRollAlbum:(BOOL)allowPickingVideo allowPickingImage:(BOOL)allowPickingImage completion:(void (^)(TZAlbumModel *))completion{
  85. __block TZAlbumModel *model;
  86. if (iOS8Later) {
  87. PHFetchOptions *option = [[PHFetchOptions alloc] init];
  88. if (!allowPickingVideo) option.predicate = [NSPredicate predicateWithFormat:@"mediaType == %ld", PHAssetMediaTypeImage];
  89. if (!allowPickingImage) option.predicate = [NSPredicate predicateWithFormat:@"mediaType == %ld",
  90. PHAssetMediaTypeVideo];
  91. // option.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"modificationDate" ascending:self.sortAscendingByModificationDate]];
  92. if (!self.sortAscendingByModificationDate) {
  93. option.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:self.sortAscendingByModificationDate]];
  94. }
  95. PHFetchResult *smartAlbums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum subtype:PHAssetCollectionSubtypeAlbumRegular options:nil];
  96. for (PHAssetCollection *collection in smartAlbums) {
  97. // 有可能是PHCollectionList类的的对象,过滤掉
  98. if (![collection isKindOfClass:[PHAssetCollection class]]) continue;
  99. if ([self isCameraRollAlbum:collection.localizedTitle]) {
  100. PHFetchResult *fetchResult = [PHAsset fetchAssetsInAssetCollection:collection options:option];
  101. model = [self modelWithResult:fetchResult name:collection.localizedTitle];
  102. if (completion) completion(model);
  103. break;
  104. }
  105. }
  106. } else {
  107. [self.assetLibrary enumerateGroupsWithTypes:ALAssetsGroupAll usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
  108. if ([group numberOfAssets] < 1) return;
  109. NSString *name = [group valueForProperty:ALAssetsGroupPropertyName];
  110. if ([self isCameraRollAlbum:name]) {
  111. model = [self modelWithResult:group name:name];
  112. if (completion) completion(model);
  113. *stop = YES;
  114. }
  115. } failureBlock:nil];
  116. }
  117. }
  118. - (void)getAllAlbums:(BOOL)allowPickingVideo allowPickingImage:(BOOL)allowPickingImage completion:(void (^)(NSArray<TZAlbumModel *> *))completion{
  119. NSMutableArray *albumArr = [NSMutableArray array];
  120. if (iOS8Later) {
  121. PHFetchOptions *option = [[PHFetchOptions alloc] init];
  122. if (!allowPickingVideo) option.predicate = [NSPredicate predicateWithFormat:@"mediaType == %ld", PHAssetMediaTypeImage];
  123. if (!allowPickingImage) option.predicate = [NSPredicate predicateWithFormat:@"mediaType == %ld",
  124. PHAssetMediaTypeVideo];
  125. // option.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"modificationDate" ascending:self.sortAscendingByModificationDate]];
  126. if (!self.sortAscendingByModificationDate) {
  127. option.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:self.sortAscendingByModificationDate]];
  128. }
  129. // 我的照片流 1.6.10重新加入..
  130. PHFetchResult *myPhotoStreamAlbum = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum subtype:PHAssetCollectionSubtypeAlbumMyPhotoStream options:nil];
  131. PHFetchResult *smartAlbums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum subtype:PHAssetCollectionSubtypeAlbumRegular options:nil];
  132. PHFetchResult *topLevelUserCollections = [PHCollectionList fetchTopLevelUserCollectionsWithOptions:nil];
  133. PHFetchResult *syncedAlbums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum subtype:PHAssetCollectionSubtypeAlbumSyncedAlbum options:nil];
  134. PHFetchResult *sharedAlbums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum subtype:PHAssetCollectionSubtypeAlbumCloudShared options:nil];
  135. NSArray *allAlbums = @[myPhotoStreamAlbum,smartAlbums,topLevelUserCollections,syncedAlbums,sharedAlbums];
  136. for (PHFetchResult *fetchResult in allAlbums) {
  137. for (PHAssetCollection *collection in fetchResult) {
  138. // 有可能是PHCollectionList类的的对象,过滤掉
  139. if (![collection isKindOfClass:[PHAssetCollection class]]) continue;
  140. PHFetchResult *fetchResult = [PHAsset fetchAssetsInAssetCollection:collection options:option];
  141. if (fetchResult.count < 1) continue;
  142. if ([collection.localizedTitle containsString:@"Deleted"] || [collection.localizedTitle isEqualToString:ASLocalizedString(@"最近删除")]) continue;
  143. if ([self isCameraRollAlbum:collection.localizedTitle]) {
  144. [albumArr insertObject:[self modelWithResult:fetchResult name:collection.localizedTitle] atIndex:0];
  145. } else {
  146. [albumArr addObject:[self modelWithResult:fetchResult name:collection.localizedTitle]];
  147. }
  148. }
  149. }
  150. if (completion && albumArr.count > 0) completion(albumArr);
  151. } else {
  152. [self.assetLibrary enumerateGroupsWithTypes:ALAssetsGroupAll usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
  153. if (group == nil) {
  154. if (completion && albumArr.count > 0) completion(albumArr);
  155. }
  156. if ([group numberOfAssets] < 1) return;
  157. NSString *name = [group valueForProperty:ALAssetsGroupPropertyName];
  158. if ([self isCameraRollAlbum:name]) {
  159. [albumArr insertObject:[self modelWithResult:group name:name] atIndex:0];
  160. } else if ([name isEqualToString:@"My Photo Stream"] || [name isEqualToString:ASLocalizedString(@"我的照片流")]) {
  161. if (albumArr.count) {
  162. [albumArr insertObject:[self modelWithResult:group name:name] atIndex:1];
  163. } else {
  164. [albumArr addObject:[self modelWithResult:group name:name]];
  165. }
  166. } else {
  167. [albumArr addObject:[self modelWithResult:group name:name]];
  168. }
  169. } failureBlock:nil];
  170. }
  171. }
  172. #pragma mark - Get Assets
  173. /// Get Assets 获得照片数组
  174. - (void)getAssetsFromFetchResult:(id)result allowPickingVideo:(BOOL)allowPickingVideo allowPickingImage:(BOOL)allowPickingImage completion:(void (^)(NSArray<TZAssetModel *> *))completion {
  175. NSMutableArray *photoArr = [NSMutableArray array];
  176. if ([result isKindOfClass:[PHFetchResult class]]) {
  177. PHFetchResult *fetchResult = (PHFetchResult *)result;
  178. [fetchResult enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  179. TZAssetModel *model = [self assetModelWithAsset:obj allowPickingVideo:allowPickingVideo allowPickingImage:allowPickingImage];
  180. if (model) {
  181. [photoArr addObject:model];
  182. }
  183. }];
  184. if (completion) completion(photoArr);
  185. } else if ([result isKindOfClass:[ALAssetsGroup class]]) {
  186. ALAssetsGroup *group = (ALAssetsGroup *)result;
  187. if (allowPickingImage && allowPickingVideo) {
  188. [group setAssetsFilter:[ALAssetsFilter allAssets]];
  189. } else if (allowPickingVideo) {
  190. [group setAssetsFilter:[ALAssetsFilter allVideos]];
  191. } else if (allowPickingImage) {
  192. [group setAssetsFilter:[ALAssetsFilter allPhotos]];
  193. }
  194. ALAssetsGroupEnumerationResultsBlock resultBlock = ^(ALAsset *result, NSUInteger index, BOOL *stop) {
  195. if (result == nil) {
  196. if (completion) completion(photoArr);
  197. }
  198. TZAssetModel *model = [self assetModelWithAsset:result allowPickingVideo:allowPickingVideo allowPickingImage:allowPickingImage];
  199. if (model) {
  200. [photoArr addObject:model];
  201. }
  202. };
  203. if (self.sortAscendingByModificationDate) {
  204. [group enumerateAssetsUsingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop) {
  205. if (resultBlock) { resultBlock(result,index,stop); }
  206. }];
  207. } else {
  208. [group enumerateAssetsWithOptions:NSEnumerationReverse usingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop) {
  209. if (resultBlock) { resultBlock(result,index,stop); }
  210. }];
  211. }
  212. }
  213. }
  214. /// Get asset at index 获得下标为index的单个照片
  215. /// if index beyond bounds, return nil in callback 如果索引越界, 在回调中返回 nil
  216. - (void)getAssetFromFetchResult:(id)result atIndex:(NSInteger)index allowPickingVideo:(BOOL)allowPickingVideo allowPickingImage:(BOOL)allowPickingImage completion:(void (^)(TZAssetModel *))completion {
  217. if ([result isKindOfClass:[PHFetchResult class]]) {
  218. PHFetchResult *fetchResult = (PHFetchResult *)result;
  219. PHAsset *asset;
  220. @try {
  221. asset = fetchResult[index];
  222. }
  223. @catch (NSException* e) {
  224. if (completion) completion(nil);
  225. return;
  226. }
  227. TZAssetModel *model = [self assetModelWithAsset:asset allowPickingVideo:allowPickingVideo allowPickingImage:allowPickingImage];
  228. if (completion) completion(model);
  229. } else if ([result isKindOfClass:[ALAssetsGroup class]]) {
  230. ALAssetsGroup *group = (ALAssetsGroup *)result;
  231. if (allowPickingImage && allowPickingVideo) {
  232. [group setAssetsFilter:[ALAssetsFilter allAssets]];
  233. } else if (allowPickingVideo) {
  234. [group setAssetsFilter:[ALAssetsFilter allVideos]];
  235. } else if (allowPickingImage) {
  236. [group setAssetsFilter:[ALAssetsFilter allPhotos]];
  237. }
  238. NSIndexSet *indexSet = [NSIndexSet indexSetWithIndex:index];
  239. @try {
  240. [group enumerateAssetsAtIndexes:indexSet options:NSEnumerationConcurrent usingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop) {
  241. if (!result) return;
  242. TZAssetModel *model = [self assetModelWithAsset:result allowPickingVideo:allowPickingVideo allowPickingImage:allowPickingImage];
  243. if (completion) completion(model);
  244. }];
  245. }
  246. @catch (NSException* e) {
  247. if (completion) completion(nil);
  248. }
  249. }
  250. }
  251. - (TZAssetModel *)assetModelWithAsset:(id)asset allowPickingVideo:(BOOL)allowPickingVideo allowPickingImage:(BOOL)allowPickingImage {
  252. TZAssetModel *model;
  253. TZAssetModelMediaType type = TZAssetModelMediaTypePhoto;
  254. if ([asset isKindOfClass:[PHAsset class]]) {
  255. PHAsset *phAsset = (PHAsset *)asset;
  256. if (phAsset.mediaType == PHAssetMediaTypeVideo) type = TZAssetModelMediaTypeVideo;
  257. else if (phAsset.mediaType == PHAssetMediaTypeAudio) type = TZAssetModelMediaTypeAudio;
  258. else if (phAsset.mediaType == PHAssetMediaTypeImage) {
  259. if (iOS9_1Later) {
  260. // if (asset.mediaSubtypes == PHAssetMediaSubtypePhotoLive) type = TZAssetModelMediaTypeLivePhoto;
  261. }
  262. // Gif
  263. if ([[phAsset valueForKey:@"filename"] hasSuffix:@"GIF"]) {
  264. type = TZAssetModelMediaTypePhotoGif;
  265. }
  266. }
  267. if (!allowPickingVideo && type == TZAssetModelMediaTypeVideo) return nil;
  268. if (!allowPickingImage && type == TZAssetModelMediaTypePhoto) return nil;
  269. if (!allowPickingImage && type == TZAssetModelMediaTypePhotoGif) return nil;
  270. if (self.hideWhenCanNotSelect) {
  271. // 过滤掉尺寸不满足要求的图片
  272. if (![self isPhotoSelectableWithAsset:phAsset]) {
  273. return nil;
  274. }
  275. }
  276. NSString *timeLength = type == TZAssetModelMediaTypeVideo ? [NSString stringWithFormat:@"%0.0f",phAsset.duration] : @"";
  277. timeLength = [self getNewTimeFromDurationSecond:timeLength.integerValue];
  278. model = [TZAssetModel modelWithAsset:asset type:type timeLength:timeLength];
  279. } else {
  280. if (!allowPickingVideo){
  281. model = [TZAssetModel modelWithAsset:asset type:type];
  282. return model;
  283. }
  284. /// Allow picking video
  285. if ([[asset valueForProperty:ALAssetPropertyType] isEqualToString:ALAssetTypeVideo]) {
  286. type = TZAssetModelMediaTypeVideo;
  287. NSTimeInterval duration = [[asset valueForProperty:ALAssetPropertyDuration] integerValue];
  288. NSString *timeLength = [NSString stringWithFormat:@"%0.0f",duration];
  289. timeLength = [self getNewTimeFromDurationSecond:timeLength.integerValue];
  290. model = [TZAssetModel modelWithAsset:asset type:type timeLength:timeLength];
  291. } else {
  292. if (self.hideWhenCanNotSelect) {
  293. // 过滤掉尺寸不满足要求的图片
  294. if (![self isPhotoSelectableWithAsset:asset]) {
  295. return nil;
  296. }
  297. }
  298. model = [TZAssetModel modelWithAsset:asset type:type];
  299. }
  300. }
  301. return model;
  302. }
  303. - (NSString *)getNewTimeFromDurationSecond:(NSInteger)duration {
  304. NSString *newTime;
  305. if (duration < 10) {
  306. newTime = [NSString stringWithFormat:@"0:0%zd",duration];
  307. } else if (duration < 60) {
  308. newTime = [NSString stringWithFormat:@"0:%zd",duration];
  309. } else {
  310. NSInteger min = duration / 60;
  311. NSInteger sec = duration - (min * 60);
  312. if (sec < 10) {
  313. newTime = [NSString stringWithFormat:@"%zd:0%zd",min,sec];
  314. } else {
  315. newTime = [NSString stringWithFormat:@"%zd:%zd",min,sec];
  316. }
  317. }
  318. return newTime;
  319. }
  320. /// Get photo bytes 获得一组照片的大小
  321. - (void)getPhotosBytesWithArray:(NSArray *)photos completion:(void (^)(NSString *totalBytes))completion {
  322. __block NSInteger dataLength = 0;
  323. __block NSInteger assetCount = 0;
  324. for (NSInteger i = 0; i < photos.count; i++) {
  325. TZAssetModel *model = photos[i];
  326. if ([model.asset isKindOfClass:[PHAsset class]]) {
  327. [[PHImageManager defaultManager] requestImageDataForAsset:model.asset options:nil resultHandler:^(NSData * _Nullable imageData, NSString * _Nullable dataUTI, UIImageOrientation orientation, NSDictionary * _Nullable info) {
  328. if (model.type != TZAssetModelMediaTypeVideo) dataLength += imageData.length;
  329. assetCount ++;
  330. if (assetCount >= photos.count) {
  331. NSString *bytes = [self getBytesFromDataLength:dataLength];
  332. if (completion) completion(bytes);
  333. }
  334. }];
  335. } else if ([model.asset isKindOfClass:[ALAsset class]]) {
  336. ALAssetRepresentation *representation = [model.asset defaultRepresentation];
  337. if (model.type != TZAssetModelMediaTypeVideo) dataLength += (NSInteger)representation.size;
  338. if (i >= photos.count - 1) {
  339. NSString *bytes = [self getBytesFromDataLength:dataLength];
  340. if (completion) completion(bytes);
  341. }
  342. }
  343. }
  344. }
  345. - (NSString *)getBytesFromDataLength:(NSInteger)dataLength {
  346. NSString *bytes;
  347. if (dataLength >= 0.1 * (1024 * 1024)) {
  348. bytes = [NSString stringWithFormat:@"%0.1fM",dataLength/1024/1024.0];
  349. } else if (dataLength >= 1024) {
  350. bytes = [NSString stringWithFormat:@"%0.0fK",dataLength/1024.0];
  351. } else {
  352. bytes = [NSString stringWithFormat:@"%zdB",dataLength];
  353. }
  354. return bytes;
  355. }
  356. #pragma mark - Get Photo
  357. /// Get photo 获得照片本身
  358. - (PHImageRequestID)getPhotoWithAsset:(id)asset completion:(void (^)(UIImage *, NSDictionary *, BOOL isDegraded))completion {
  359. CGFloat fullScreenWidth = TZScreenWidth;
  360. if (fullScreenWidth > _photoPreviewMaxWidth) {
  361. fullScreenWidth = _photoPreviewMaxWidth;
  362. }
  363. return [self getPhotoWithAsset:asset photoWidth:fullScreenWidth completion:completion progressHandler:nil networkAccessAllowed:YES];
  364. }
  365. - (PHImageRequestID)getPhotoWithAsset:(id)asset photoWidth:(CGFloat)photoWidth completion:(void (^)(UIImage *photo,NSDictionary *info,BOOL isDegraded))completion {
  366. return [self getPhotoWithAsset:asset photoWidth:photoWidth completion:completion progressHandler:nil networkAccessAllowed:YES];
  367. }
  368. - (PHImageRequestID)getPhotoWithAsset:(id)asset completion:(void (^)(UIImage *photo,NSDictionary *info,BOOL isDegraded))completion progressHandler:(void (^)(double progress, NSError *error, BOOL *stop, NSDictionary *info))progressHandler networkAccessAllowed:(BOOL)networkAccessAllowed {
  369. CGFloat fullScreenWidth = TZScreenWidth;
  370. if (fullScreenWidth > _photoPreviewMaxWidth) {
  371. fullScreenWidth = _photoPreviewMaxWidth;
  372. }
  373. return [self getPhotoWithAsset:asset photoWidth:fullScreenWidth completion:completion progressHandler:progressHandler networkAccessAllowed:networkAccessAllowed];
  374. }
  375. - (PHImageRequestID)getPhotoWithAsset:(id)asset photoWidth:(CGFloat)photoWidth completion:(void (^)(UIImage *photo,NSDictionary *info,BOOL isDegraded))completion progressHandler:(void (^)(double progress, NSError *error, BOOL *stop, NSDictionary *info))progressHandler networkAccessAllowed:(BOOL)networkAccessAllowed {
  376. if ([asset isKindOfClass:[PHAsset class]]) {
  377. CGSize imageSize;
  378. if (photoWidth < TZScreenWidth && photoWidth < _photoPreviewMaxWidth) {
  379. imageSize = AssetGridThumbnailSize;
  380. } else {
  381. PHAsset *phAsset = (PHAsset *)asset;
  382. CGFloat aspectRatio = phAsset.pixelWidth / (CGFloat)phAsset.pixelHeight;
  383. CGFloat pixelWidth = photoWidth * TZScreenScale;
  384. CGFloat pixelHeight = pixelWidth / aspectRatio;
  385. imageSize = CGSizeMake(pixelWidth, pixelHeight);
  386. }
  387. // 修复获取图片时出现的瞬间内存过高问题
  388. // 下面两行代码,来自hsjcom,他的github是:https://github.com/hsjcom 表示感谢
  389. PHImageRequestOptions *option = [[PHImageRequestOptions alloc] init];
  390. option.resizeMode = PHImageRequestOptionsResizeModeFast;
  391. PHImageRequestID imageRequestID = [[PHImageManager defaultManager] requestImageForAsset:asset targetSize:imageSize contentMode:PHImageContentModeAspectFill options:option resultHandler:^(UIImage * _Nullable result, NSDictionary * _Nullable info) {
  392. BOOL downloadFinined = (![[info objectForKey:PHImageCancelledKey] boolValue] && ![info objectForKey:PHImageErrorKey]);
  393. if (downloadFinined && result) {
  394. result = [self fixOrientation:result];
  395. if (completion) completion(result,info,[[info objectForKey:PHImageResultIsDegradedKey] boolValue]);
  396. }
  397. // Download image from iCloud / 从iCloud下载图片
  398. if ([info objectForKey:PHImageResultIsInCloudKey] && !result && networkAccessAllowed) {
  399. PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init];
  400. options.progressHandler = ^(double progress, NSError *error, BOOL *stop, NSDictionary *info) {
  401. dispatch_async(dispatch_get_main_queue(), ^{
  402. if (progressHandler) {
  403. progressHandler(progress, error, stop, info);
  404. }
  405. });
  406. };
  407. options.networkAccessAllowed = YES;
  408. options.resizeMode = PHImageRequestOptionsResizeModeFast;
  409. [[PHImageManager defaultManager] requestImageDataForAsset:asset options:options resultHandler:^(NSData * _Nullable imageData, NSString * _Nullable dataUTI, UIImageOrientation orientation, NSDictionary * _Nullable info) {
  410. UIImage *resultImage = [UIImage imageWithData:imageData scale:0.1];
  411. resultImage = [self scaleImage:resultImage toSize:imageSize];
  412. if (resultImage) {
  413. resultImage = [self fixOrientation:resultImage];
  414. if (completion) completion(resultImage,info,[[info objectForKey:PHImageResultIsDegradedKey] boolValue]);
  415. }
  416. }];
  417. }
  418. }];
  419. return imageRequestID;
  420. } else if ([asset isKindOfClass:[ALAsset class]]) {
  421. ALAsset *alAsset = (ALAsset *)asset;
  422. dispatch_async(dispatch_get_global_queue(0,0), ^{
  423. CGImageRef thumbnailImageRef = alAsset.thumbnail;
  424. UIImage *thumbnailImage = [UIImage imageWithCGImage:thumbnailImageRef scale:2.0 orientation:UIImageOrientationUp];
  425. dispatch_async(dispatch_get_main_queue(), ^{
  426. if (completion) completion(thumbnailImage,nil,YES);
  427. if (photoWidth == TZScreenWidth || photoWidth == _photoPreviewMaxWidth) {
  428. dispatch_async(dispatch_get_global_queue(0,0), ^{
  429. ALAssetRepresentation *assetRep = [alAsset defaultRepresentation];
  430. CGImageRef fullScrennImageRef = [assetRep fullScreenImage];
  431. UIImage *fullScrennImage = [UIImage imageWithCGImage:fullScrennImageRef scale:2.0 orientation:UIImageOrientationUp];
  432. dispatch_async(dispatch_get_main_queue(), ^{
  433. if (completion) completion(fullScrennImage,nil,NO);
  434. });
  435. });
  436. }
  437. });
  438. });
  439. }
  440. return 0;
  441. }
  442. /// Get postImage / 获取封面图
  443. - (void)getPostImageWithAlbumModel:(TZAlbumModel *)model completion:(void (^)(UIImage *))completion {
  444. if (iOS8Later) {
  445. id asset = [model.result lastObject];
  446. if (!self.sortAscendingByModificationDate) {
  447. asset = [model.result firstObject];
  448. }
  449. [[TZImageManager manager] getPhotoWithAsset:asset photoWidth:80 completion:^(UIImage *photo, NSDictionary *info, BOOL isDegraded) {
  450. if (completion) completion(photo);
  451. }];
  452. } else {
  453. ALAssetsGroup *group = model.result;
  454. UIImage *postImage = [UIImage imageWithCGImage:group.posterImage];
  455. if (completion) completion(postImage);
  456. }
  457. }
  458. /// Get Original Photo / 获取原图
  459. - (void)getOriginalPhotoWithAsset:(id)asset completion:(void (^)(UIImage *photo,NSDictionary *info))completion {
  460. [self getOriginalPhotoWithAsset:asset newCompletion:^(UIImage *photo, NSDictionary *info, BOOL isDegraded) {
  461. if (completion) {
  462. completion(photo,info);
  463. }
  464. }];
  465. }
  466. - (void)getOriginalPhotoWithAsset:(id)asset newCompletion:(void (^)(UIImage *photo,NSDictionary *info,BOOL isDegraded))completion {
  467. if ([asset isKindOfClass:[PHAsset class]]) {
  468. PHImageRequestOptions *option = [[PHImageRequestOptions alloc]init];
  469. option.networkAccessAllowed = YES;
  470. [[PHImageManager defaultManager] requestImageForAsset:asset targetSize:PHImageManagerMaximumSize contentMode:PHImageContentModeAspectFit options:option resultHandler:^(UIImage * _Nullable result, NSDictionary * _Nullable info) {
  471. BOOL downloadFinined = (![[info objectForKey:PHImageCancelledKey] boolValue] && ![info objectForKey:PHImageErrorKey]);
  472. if (downloadFinined && result) {
  473. result = [self fixOrientation:result];
  474. BOOL isDegraded = [[info objectForKey:PHImageResultIsDegradedKey] boolValue];
  475. if (completion) completion(result,info,isDegraded);
  476. }
  477. }];
  478. } else if ([asset isKindOfClass:[ALAsset class]]) {
  479. ALAsset *alAsset = (ALAsset *)asset;
  480. ALAssetRepresentation *assetRep = [alAsset defaultRepresentation];
  481. dispatch_async(dispatch_get_global_queue(0,0), ^{
  482. CGImageRef originalImageRef = [assetRep fullResolutionImage];
  483. UIImage *originalImage = [UIImage imageWithCGImage:originalImageRef scale:1.0 orientation:UIImageOrientationUp];
  484. dispatch_async(dispatch_get_main_queue(), ^{
  485. if (completion) completion(originalImage,nil,NO);
  486. });
  487. });
  488. }
  489. }
  490. - (void)getOriginalPhotoDataWithAsset:(id)asset completion:(void (^)(NSData *data,NSDictionary *info,BOOL isDegraded))completion {
  491. if ([asset isKindOfClass:[PHAsset class]]) {
  492. PHImageRequestOptions *option = [[PHImageRequestOptions alloc]init];
  493. option.networkAccessAllowed = YES;
  494. [[PHImageManager defaultManager] requestImageDataForAsset:asset options:option resultHandler:^(NSData * _Nullable imageData, NSString * _Nullable dataUTI, UIImageOrientation orientation, NSDictionary * _Nullable info) {
  495. BOOL downloadFinined = (![[info objectForKey:PHImageCancelledKey] boolValue] && ![info objectForKey:PHImageErrorKey]);
  496. if (downloadFinined && imageData) {
  497. BOOL isDegraded = [[info objectForKey:PHImageResultIsDegradedKey] boolValue];
  498. if (completion) completion(imageData,info,isDegraded);
  499. }
  500. }];
  501. } else if ([asset isKindOfClass:[ALAsset class]]) {
  502. ALAsset *alAsset = (ALAsset *)asset;
  503. ALAssetRepresentation *assetRep = [alAsset defaultRepresentation];
  504. Byte *imageBuffer = (Byte *)malloc(assetRep.size);
  505. NSUInteger bufferSize = [assetRep getBytes:imageBuffer fromOffset:0.0 length:assetRep.size error:nil];
  506. NSData *imageData = [NSData dataWithBytesNoCopy:imageBuffer length:bufferSize freeWhenDone:YES];
  507. if (completion) completion(imageData,nil,NO);
  508. }
  509. }
  510. #pragma mark - Save photo
  511. - (void)savePhotoWithImage:(UIImage *)image completion:(void (^)(NSError *error))completion {
  512. NSData *data = UIImageJPEGRepresentation(image, 0.9);
  513. if (iOS9Later) { // 这里有坑... iOS8系统下这个方法保存图片会失败 原来是因为PHAssetResourceType是iOS9之后的...
  514. [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
  515. PHAssetResourceCreationOptions *options = [[PHAssetResourceCreationOptions alloc] init];
  516. options.shouldMoveFile = YES;
  517. [[PHAssetCreationRequest creationRequestForAsset] addResourceWithType:PHAssetResourceTypePhoto data:data options:options];
  518. } completionHandler:^(BOOL success, NSError * _Nullable error) {
  519. dispatch_sync(dispatch_get_main_queue(), ^{
  520. if (success && completion) {
  521. completion(nil);
  522. } else if (error) {
  523. NSLog(ASLocalizedString(@"保存照片出错:%@"),error.localizedDescription);
  524. if (completion) {
  525. completion(error);
  526. }
  527. }
  528. });
  529. }];
  530. } else {
  531. [self.assetLibrary writeImageToSavedPhotosAlbum:image.CGImage orientation:[self orientationFromImage:image] completionBlock:^(NSURL *assetURL, NSError *error) {
  532. if (error) {
  533. NSLog(ASLocalizedString(@"保存图片失败:%@"),error.localizedDescription);
  534. if (completion) {
  535. completion(error);
  536. }
  537. } else {
  538. // 多给系统0.5秒的时间,让系统去更新相册数据
  539. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  540. if (completion) {
  541. completion(nil);
  542. }
  543. });
  544. }
  545. }];
  546. }
  547. }
  548. #pragma mark - Get Video
  549. /// Get Video / 获取视频
  550. - (void)getVideoWithAsset:(id)asset completion:(void (^)(AVPlayerItem * _Nullable, NSDictionary * _Nullable))completion {
  551. if ([asset isKindOfClass:[PHAsset class]]) {
  552. [[PHImageManager defaultManager] requestPlayerItemForVideo:asset options:nil resultHandler:^(AVPlayerItem * _Nullable playerItem, NSDictionary * _Nullable info) {
  553. if (completion) completion(playerItem,info);
  554. }];
  555. } else if ([asset isKindOfClass:[ALAsset class]]) {
  556. ALAsset *alAsset = (ALAsset *)asset;
  557. ALAssetRepresentation *defaultRepresentation = [alAsset defaultRepresentation];
  558. NSString *uti = [defaultRepresentation UTI];
  559. NSURL *videoURL = [[asset valueForProperty:ALAssetPropertyURLs] valueForKey:uti];
  560. AVPlayerItem *playerItem = [[AVPlayerItem alloc] initWithURL:videoURL];
  561. if (completion && playerItem) completion(playerItem,nil);
  562. }
  563. }
  564. #pragma mark - Export video
  565. /// Export Video / 导出视频
  566. - (void)getVideoOutputPathWithAsset:(id)asset completion:(void (^)(NSString *outputPath))completion {
  567. if ([asset isKindOfClass:[PHAsset class]]) {
  568. PHVideoRequestOptions* options = [[PHVideoRequestOptions alloc] init];
  569. options.version = PHVideoRequestOptionsVersionOriginal;
  570. options.deliveryMode = PHVideoRequestOptionsDeliveryModeAutomatic;
  571. options.networkAccessAllowed = YES;
  572. [[PHImageManager defaultManager] requestAVAssetForVideo:asset options:options resultHandler:^(AVAsset* avasset, AVAudioMix* audioMix, NSDictionary* info){
  573. // NSLog(@"Info:\n%@",info);
  574. AVURLAsset *videoAsset = (AVURLAsset*)avasset;
  575. // NSLog(@"AVAsset URL: %@",myAsset.URL);
  576. [self startExportVideoWithVideoAsset:videoAsset completion:completion];
  577. }];
  578. } else if ([asset isKindOfClass:[ALAsset class]]) {
  579. NSURL *videoURL =[asset valueForProperty:ALAssetPropertyAssetURL]; // ALAssetPropertyURLs
  580. AVURLAsset *videoAsset = [[AVURLAsset alloc] initWithURL:videoURL options:nil];
  581. [self startExportVideoWithVideoAsset:videoAsset completion:completion];
  582. }
  583. }
  584. - (void)startExportVideoWithVideoAsset:(AVURLAsset *)videoAsset completion:(void (^)(NSString *outputPath))completion {
  585. // Find compatible presets by video asset.
  586. NSArray *presets = [AVAssetExportSession exportPresetsCompatibleWithAsset:videoAsset];
  587. // Begin to compress video
  588. // Now we just compress to low resolution if it supports
  589. // If you need to upload to the server, but server does't support to upload by streaming,
  590. // You can compress the resolution to lower. Or you can support more higher resolution.
  591. if ([presets containsObject:AVAssetExportPreset640x480]) {
  592. AVAssetExportSession *session = [[AVAssetExportSession alloc]initWithAsset:videoAsset presetName:AVAssetExportPreset640x480];
  593. NSDateFormatter *formater = [[NSDateFormatter alloc] init];
  594. [formater setDateFormat:@"yyyy-MM-dd-HH:mm:ss"];
  595. NSString *outputPath = [NSHomeDirectory() stringByAppendingFormat:@"/tmp/output-%@.mp4", [formater stringFromDate:[NSDate date]]];
  596. NSLog(@"video outputPath = %@",outputPath);
  597. session.outputURL = [NSURL fileURLWithPath:outputPath];
  598. // Optimize for network use.
  599. session.shouldOptimizeForNetworkUse = true;
  600. NSArray *supportedTypeArray = session.supportedFileTypes;
  601. if ([supportedTypeArray containsObject:AVFileTypeMPEG4]) {
  602. session.outputFileType = AVFileTypeMPEG4;
  603. } else if (supportedTypeArray.count == 0) {
  604. NSLog(ASLocalizedString(@"No supported file types 视频类型暂不支持导出"));
  605. return;
  606. } else {
  607. session.outputFileType = [supportedTypeArray objectAtIndex:0];
  608. }
  609. if (![[NSFileManager defaultManager] fileExistsAtPath:[NSHomeDirectory() stringByAppendingFormat:@"/tmp"]]) {
  610. [[NSFileManager defaultManager] createDirectoryAtPath:[NSHomeDirectory() stringByAppendingFormat:@"/tmp"] withIntermediateDirectories:YES attributes:nil error:nil];
  611. }
  612. AVMutableVideoComposition *videoComposition = [self fixedCompositionWithAsset:videoAsset];
  613. if (videoComposition.renderSize.width) {
  614. // 修正视频转向
  615. session.videoComposition = videoComposition;
  616. }
  617. // Begin to export video to the output path asynchronously.
  618. [session exportAsynchronouslyWithCompletionHandler:^(void) {
  619. switch (session.status) {
  620. case AVAssetExportSessionStatusUnknown:
  621. NSLog(@"AVAssetExportSessionStatusUnknown"); break;
  622. case AVAssetExportSessionStatusWaiting:
  623. NSLog(@"AVAssetExportSessionStatusWaiting"); break;
  624. case AVAssetExportSessionStatusExporting:
  625. NSLog(@"AVAssetExportSessionStatusExporting"); break;
  626. case AVAssetExportSessionStatusCompleted: {
  627. NSLog(@"AVAssetExportSessionStatusCompleted");
  628. dispatch_async(dispatch_get_main_queue(), ^{
  629. if (completion) {
  630. completion(outputPath);
  631. }
  632. });
  633. } break;
  634. case AVAssetExportSessionStatusFailed:
  635. NSLog(@"AVAssetExportSessionStatusFailed"); break;
  636. default: break;
  637. }
  638. }];
  639. }
  640. }
  641. /// Judge is a assets array contain the asset 判断一个assets数组是否包含这个asset
  642. - (BOOL)isAssetsArray:(NSArray *)assets containAsset:(id)asset {
  643. if (iOS8Later) {
  644. return [assets containsObject:asset];
  645. } else {
  646. NSMutableArray *selectedAssetUrls = [NSMutableArray array];
  647. for (ALAsset *asset_item in assets) {
  648. [selectedAssetUrls addObject:[asset_item valueForProperty:ALAssetPropertyURLs]];
  649. }
  650. return [selectedAssetUrls containsObject:[asset valueForProperty:ALAssetPropertyURLs]];
  651. }
  652. }
  653. - (BOOL)isCameraRollAlbum:(NSString *)albumName {
  654. NSString *versionStr = [[UIDevice currentDevice].systemVersion stringByReplacingOccurrencesOfString:@"." withString:@""];
  655. if (versionStr.length <= 1) {
  656. versionStr = [versionStr stringByAppendingString:@"00"];
  657. } else if (versionStr.length <= 2) {
  658. versionStr = [versionStr stringByAppendingString:@"0"];
  659. }
  660. CGFloat version = versionStr.floatValue;
  661. // 目前已知8.0.0 - 8.0.2系统,拍照后的图片会保存在最近添加中
  662. if (version >= 800 && version <= 802) {
  663. return [albumName isEqualToString:ASLocalizedString(@"最近添加")] || [albumName isEqualToString:@"Recently Added"];
  664. } else {
  665. return [albumName isEqualToString:@"Camera Roll"] || [albumName isEqualToString:ASLocalizedString(@"相机胶卷")] || [albumName isEqualToString:ASLocalizedString(@"所有照片")] || [albumName isEqualToString:@"All Photos"];
  666. }
  667. }
  668. - (NSString *)getAssetIdentifier:(id)asset {
  669. if (iOS8Later) {
  670. PHAsset *phAsset = (PHAsset *)asset;
  671. return phAsset.localIdentifier;
  672. } else {
  673. ALAsset *alAsset = (ALAsset *)asset;
  674. NSURL *assetUrl = [alAsset valueForProperty:ALAssetPropertyAssetURL];
  675. return assetUrl.absoluteString;
  676. }
  677. }
  678. /// 检查照片大小是否满足最小要求
  679. - (BOOL)isPhotoSelectableWithAsset:(id)asset {
  680. CGSize photoSize = [self photoSizeWithAsset:asset];
  681. if (self.minPhotoWidthSelectable > photoSize.width || self.minPhotoHeightSelectable > photoSize.height) {
  682. return NO;
  683. }
  684. return YES;
  685. }
  686. - (CGSize)photoSizeWithAsset:(id)asset {
  687. if (iOS8Later) {
  688. PHAsset *phAsset = (PHAsset *)asset;
  689. return CGSizeMake(phAsset.pixelWidth, phAsset.pixelHeight);
  690. } else {
  691. ALAsset *alAsset = (ALAsset *)asset;
  692. return alAsset.defaultRepresentation.dimensions;
  693. }
  694. }
  695. #pragma mark - Private Method
  696. - (TZAlbumModel *)modelWithResult:(id)result name:(NSString *)name{
  697. TZAlbumModel *model = [[TZAlbumModel alloc] init];
  698. model.result = result;
  699. model.name = name;
  700. if ([result isKindOfClass:[PHFetchResult class]]) {
  701. PHFetchResult *fetchResult = (PHFetchResult *)result;
  702. model.count = fetchResult.count;
  703. } else if ([result isKindOfClass:[ALAssetsGroup class]]) {
  704. ALAssetsGroup *group = (ALAssetsGroup *)result;
  705. model.count = [group numberOfAssets];
  706. }
  707. return model;
  708. }
  709. - (UIImage *)scaleImage:(UIImage *)image toSize:(CGSize)size {
  710. if (image.size.width > size.width) {
  711. UIGraphicsBeginImageContext(size);
  712. [image drawInRect:CGRectMake(0, 0, size.width, size.height)];
  713. UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
  714. UIGraphicsEndImageContext();
  715. return newImage;
  716. } else {
  717. return image;
  718. }
  719. }
  720. - (ALAssetOrientation)orientationFromImage:(UIImage *)image {
  721. NSInteger orientation = image.imageOrientation;
  722. return orientation;
  723. }
  724. /// 获取优化后的视频转向信息
  725. - (AVMutableVideoComposition *)fixedCompositionWithAsset:(AVAsset *)videoAsset {
  726. AVMutableVideoComposition *videoComposition = [AVMutableVideoComposition videoComposition];
  727. // 视频转向
  728. int degrees = [self degressFromVideoFileWithAsset:videoAsset];
  729. if (degrees != 0) {
  730. CGAffineTransform translateToCenter;
  731. CGAffineTransform mixedTransform;
  732. videoComposition.frameDuration = CMTimeMake(1, 30);
  733. NSArray *tracks = [videoAsset tracksWithMediaType:AVMediaTypeVideo];
  734. AVAssetTrack *videoTrack = [tracks objectAtIndex:0];
  735. if (degrees == 90) {
  736. // 顺时针旋转90°
  737. translateToCenter = CGAffineTransformMakeTranslation(videoTrack.naturalSize.height, 0.0);
  738. mixedTransform = CGAffineTransformRotate(translateToCenter,M_PI_2);
  739. videoComposition.renderSize = CGSizeMake(videoTrack.naturalSize.height,videoTrack.naturalSize.width);
  740. } else if(degrees == 180){
  741. // 顺时针旋转180°
  742. translateToCenter = CGAffineTransformMakeTranslation(videoTrack.naturalSize.width, videoTrack.naturalSize.height);
  743. mixedTransform = CGAffineTransformRotate(translateToCenter,M_PI);
  744. videoComposition.renderSize = CGSizeMake(videoTrack.naturalSize.width,videoTrack.naturalSize.height);
  745. } else if(degrees == 270){
  746. // 顺时针旋转270°
  747. translateToCenter = CGAffineTransformMakeTranslation(0.0, videoTrack.naturalSize.width);
  748. mixedTransform = CGAffineTransformRotate(translateToCenter,M_PI_2*3.0);
  749. videoComposition.renderSize = CGSizeMake(videoTrack.naturalSize.height,videoTrack.naturalSize.width);
  750. }
  751. AVMutableVideoCompositionInstruction *roateInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];
  752. roateInstruction.timeRange = CMTimeRangeMake(kCMTimeZero, [videoAsset duration]);
  753. AVMutableVideoCompositionLayerInstruction *roateLayerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:videoTrack];
  754. [roateLayerInstruction setTransform:mixedTransform atTime:kCMTimeZero];
  755. roateInstruction.layerInstructions = @[roateLayerInstruction];
  756. // 加入视频方向信息
  757. videoComposition.instructions = @[roateInstruction];
  758. }
  759. return videoComposition;
  760. }
  761. /// 获取视频角度
  762. - (int)degressFromVideoFileWithAsset:(AVAsset *)asset {
  763. int degress = 0;
  764. NSArray *tracks = [asset tracksWithMediaType:AVMediaTypeVideo];
  765. if([tracks count] > 0) {
  766. AVAssetTrack *videoTrack = [tracks objectAtIndex:0];
  767. CGAffineTransform t = videoTrack.preferredTransform;
  768. if(t.a == 0 && t.b == 1.0 && t.c == -1.0 && t.d == 0){
  769. // Portrait
  770. degress = 90;
  771. } else if(t.a == 0 && t.b == -1.0 && t.c == 1.0 && t.d == 0){
  772. // PortraitUpsideDown
  773. degress = 270;
  774. } else if(t.a == 1.0 && t.b == 0 && t.c == 0 && t.d == 1.0){
  775. // LandscapeRight
  776. degress = 0;
  777. } else if(t.a == -1.0 && t.b == 0 && t.c == 0 && t.d == -1.0){
  778. // LandscapeLeft
  779. degress = 180;
  780. }
  781. }
  782. return degress;
  783. }
  784. /// 修正图片转向
  785. - (UIImage *)fixOrientation:(UIImage *)aImage {
  786. if (!self.shouldFixOrientation) return aImage;
  787. // No-op if the orientation is already correct
  788. if (aImage.imageOrientation == UIImageOrientationUp)
  789. return aImage;
  790. // We need to calculate the proper transformation to make the image upright.
  791. // We do it in 2 steps: Rotate if Left/Right/Down, and then flip if Mirrored.
  792. CGAffineTransform transform = CGAffineTransformIdentity;
  793. switch (aImage.imageOrientation) {
  794. case UIImageOrientationDown:
  795. case UIImageOrientationDownMirrored:
  796. transform = CGAffineTransformTranslate(transform, aImage.size.width, aImage.size.height);
  797. transform = CGAffineTransformRotate(transform, M_PI);
  798. break;
  799. case UIImageOrientationLeft:
  800. case UIImageOrientationLeftMirrored:
  801. transform = CGAffineTransformTranslate(transform, aImage.size.width, 0);
  802. transform = CGAffineTransformRotate(transform, M_PI_2);
  803. break;
  804. case UIImageOrientationRight:
  805. case UIImageOrientationRightMirrored:
  806. transform = CGAffineTransformTranslate(transform, 0, aImage.size.height);
  807. transform = CGAffineTransformRotate(transform, -M_PI_2);
  808. break;
  809. default:
  810. break;
  811. }
  812. switch (aImage.imageOrientation) {
  813. case UIImageOrientationUpMirrored:
  814. case UIImageOrientationDownMirrored:
  815. transform = CGAffineTransformTranslate(transform, aImage.size.width, 0);
  816. transform = CGAffineTransformScale(transform, -1, 1);
  817. break;
  818. case UIImageOrientationLeftMirrored:
  819. case UIImageOrientationRightMirrored:
  820. transform = CGAffineTransformTranslate(transform, aImage.size.height, 0);
  821. transform = CGAffineTransformScale(transform, -1, 1);
  822. break;
  823. default:
  824. break;
  825. }
  826. // Now we draw the underlying CGImage into a new context, applying the transform
  827. // calculated above.
  828. CGContextRef ctx = CGBitmapContextCreate(NULL, aImage.size.width, aImage.size.height,
  829. CGImageGetBitsPerComponent(aImage.CGImage), 0,
  830. CGImageGetColorSpace(aImage.CGImage),
  831. CGImageGetBitmapInfo(aImage.CGImage));
  832. CGContextConcatCTM(ctx, transform);
  833. switch (aImage.imageOrientation) {
  834. case UIImageOrientationLeft:
  835. case UIImageOrientationLeftMirrored:
  836. case UIImageOrientationRight:
  837. case UIImageOrientationRightMirrored:
  838. // Grr...
  839. CGContextDrawImage(ctx, CGRectMake(0,0,aImage.size.height,aImage.size.width), aImage.CGImage);
  840. break;
  841. default:
  842. CGContextDrawImage(ctx, CGRectMake(0,0,aImage.size.width,aImage.size.height), aImage.CGImage);
  843. break;
  844. }
  845. // And now we just create a new UIImage from the drawing context
  846. CGImageRef cgimg = CGBitmapContextCreateImage(ctx);
  847. UIImage *img = [UIImage imageWithCGImage:cgimg];
  848. CGContextRelease(ctx);
  849. CGImageRelease(cgimg);
  850. return img;
  851. }
  852. #pragma clang diagnostic pop
  853. @end
  854. //@implementation TZSortDescriptor
  855. //
  856. //- (id)reversedSortDescriptor {
  857. // return [NSNumber numberWithBool:![TZImageManager manager].sortAscendingByModificationDate];
  858. //}
  859. //
  860. //@end