TZImageManager.m 45 KB

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