// // TZImageManager.m // TZImagePickerController // // Created by 谭真 on 16/1/4. // Copyright © 2016年 谭真. All rights reserved. // #import "TZImageManager.h" #import #import "TZAssetModel.h" #import "TZImagePickerController.h" @interface TZImageManager () #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" @property (nonatomic, strong) ALAssetsLibrary *assetLibrary; @end @implementation TZImageManager CGSize AssetGridThumbnailSize; CGFloat TZScreenWidth; CGFloat TZScreenScale; static TZImageManager *manager; static dispatch_once_t onceToken; + (instancetype)manager { dispatch_once(&onceToken, ^{ manager = [[self alloc] init]; if (iOS8Later) { // manager.cachingImageManager = [[PHCachingImageManager alloc] init]; // manager.cachingImageManager.allowsCachingHighQualityImages = YES; } [manager configTZScreenWidth]; }); return manager; } + (void)deallocManager { onceToken = 0; manager = nil; } - (void)setPhotoWidth:(CGFloat)photoWidth { _photoWidth = photoWidth; TZScreenWidth = photoWidth / 2; } - (void)setColumnNumber:(NSInteger)columnNumber { [self configTZScreenWidth]; _columnNumber = columnNumber; CGFloat margin = 4; CGFloat itemWH = (TZScreenWidth - 2 * margin - 4) / columnNumber - margin; AssetGridThumbnailSize = CGSizeMake(itemWH * TZScreenScale, itemWH * TZScreenScale); } - (void)configTZScreenWidth { TZScreenWidth = [UIScreen mainScreen].bounds.size.width; // 测试发现,如果scale在plus真机上取到3.0,内存会增大特别多。故这里写死成2.0 TZScreenScale = 2.0; if (TZScreenWidth > 700) { TZScreenScale = 1.5; } } - (ALAssetsLibrary *)assetLibrary { if (_assetLibrary == nil) _assetLibrary = [[ALAssetsLibrary alloc] init]; return _assetLibrary; } /// Return YES if Authorized 返回YES如果得到了授权 - (BOOL)authorizationStatusAuthorized { NSInteger status = [self.class authorizationStatus]; if (status == 0) { /** * 当某些情况下AuthorizationStatus == AuthorizationStatusNotDetermined时,无法弹出系统首次使用的授权alertView,系统应用设置里亦没有相册的设置,此时将无法使用,故作以下操作,弹出系统首次使用的授权alertView */ [self requestAuthorizationWithCompletion:nil]; } return status == 3; } + (NSInteger)authorizationStatus { if (iOS8Later) { return [PHPhotoLibrary authorizationStatus]; } else { return [ALAssetsLibrary authorizationStatus]; } return NO; } - (void)requestAuthorizationWithCompletion:(void (^)(void))completion { void (^callCompletionBlock)(void) = ^(){ dispatch_async(dispatch_get_main_queue(), ^{ if (completion) { completion(); } }); }; if (iOS8Later) { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) { callCompletionBlock(); }]; }); } else { [self.assetLibrary enumerateGroupsWithTypes:ALAssetsGroupAll usingBlock:^(ALAssetsGroup *group, BOOL *stop) { callCompletionBlock(); } failureBlock:^(NSError *error) { callCompletionBlock(); }]; } } #pragma mark - Get Album /// Get Album 获得相册/相册数组 - (void)getCameraRollAlbum:(BOOL)allowPickingVideo allowPickingImage:(BOOL)allowPickingImage needFetchAssets:(BOOL)needFetchAssets completion:(void (^)(TZAlbumModel *model))completion { __block TZAlbumModel *model; if (iOS8Later) { PHFetchOptions *option = [[PHFetchOptions alloc] init]; if (!allowPickingVideo) option.predicate = [NSPredicate predicateWithFormat:@"mediaType == %ld", PHAssetMediaTypeImage]; if (!allowPickingImage) option.predicate = [NSPredicate predicateWithFormat:@"mediaType == %ld", PHAssetMediaTypeVideo]; // option.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"modificationDate" ascending:self.sortAscendingByModificationDate]]; if (!self.sortAscendingByModificationDate) { option.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:self.sortAscendingByModificationDate]]; } PHFetchResult *smartAlbums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum subtype:PHAssetCollectionSubtypeAlbumRegular options:nil]; for (PHAssetCollection *collection in smartAlbums) { // 有可能是PHCollectionList类的的对象,过滤掉 if (![collection isKindOfClass:[PHAssetCollection class]]) continue; // 过滤空相册 if (collection.estimatedAssetCount <= 0) continue; if ([self isCameraRollAlbum:collection]) { PHFetchResult *fetchResult = [PHAsset fetchAssetsInAssetCollection:collection options:option]; model = [self modelWithResult:fetchResult name:collection.localizedTitle isCameraRoll:YES needFetchAssets:needFetchAssets]; if (completion) completion(model); break; } } } else { [self.assetLibrary enumerateGroupsWithTypes:ALAssetsGroupAll usingBlock:^(ALAssetsGroup *group, BOOL *stop) { if ([group numberOfAssets] < 1) return; if ([self isCameraRollAlbum:group]) { NSString *name = [group valueForProperty:ALAssetsGroupPropertyName]; model = [self modelWithResult:group name:name isCameraRoll:YES needFetchAssets:needFetchAssets]; if (completion) completion(model); *stop = YES; } } failureBlock:nil]; } } - (void)getAllAlbums:(BOOL)allowPickingVideo allowPickingImage:(BOOL)allowPickingImage needFetchAssets:(BOOL)needFetchAssets completion:(void (^)(NSArray *))completion{ NSMutableArray *albumArr = [NSMutableArray array]; if (iOS8Later) { PHFetchOptions *option = [[PHFetchOptions alloc] init]; if (!allowPickingVideo) option.predicate = [NSPredicate predicateWithFormat:@"mediaType == %ld", PHAssetMediaTypeImage]; if (!allowPickingImage) option.predicate = [NSPredicate predicateWithFormat:@"mediaType == %ld", PHAssetMediaTypeVideo]; // option.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"modificationDate" ascending:self.sortAscendingByModificationDate]]; if (!self.sortAscendingByModificationDate) { option.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:self.sortAscendingByModificationDate]]; } // 我的照片流 1.6.10重新加入.. PHFetchResult *myPhotoStreamAlbum = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum subtype:PHAssetCollectionSubtypeAlbumMyPhotoStream options:nil]; PHFetchResult *smartAlbums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum subtype:PHAssetCollectionSubtypeAlbumRegular options:nil]; PHFetchResult *topLevelUserCollections = [PHCollectionList fetchTopLevelUserCollectionsWithOptions:nil]; PHFetchResult *syncedAlbums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum subtype:PHAssetCollectionSubtypeAlbumSyncedAlbum options:nil]; PHFetchResult *sharedAlbums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum subtype:PHAssetCollectionSubtypeAlbumCloudShared options:nil]; NSArray *allAlbums = @[myPhotoStreamAlbum,smartAlbums,topLevelUserCollections,syncedAlbums,sharedAlbums]; for (PHFetchResult *fetchResult in allAlbums) { for (PHAssetCollection *collection in fetchResult) { // 有可能是PHCollectionList类的的对象,过滤掉 if (![collection isKindOfClass:[PHAssetCollection class]]) continue; // 过滤空相册 if (collection.estimatedAssetCount <= 0) continue; PHFetchResult *fetchResult = [PHAsset fetchAssetsInAssetCollection:collection options:option]; if (fetchResult.count < 1) continue; if ([self.pickerDelegate respondsToSelector:@selector(isAlbumCanSelect:result:)]) { if (![self.pickerDelegate isAlbumCanSelect:collection.localizedTitle result:fetchResult]) { continue; } } if (collection.assetCollectionSubtype == PHAssetCollectionSubtypeSmartAlbumAllHidden) continue; if (collection.assetCollectionSubtype == 1000000201) continue; //『最近删除』相册 if ([self isCameraRollAlbum:collection]) { [albumArr insertObject:[self modelWithResult:fetchResult name:collection.localizedTitle isCameraRoll:YES needFetchAssets:needFetchAssets] atIndex:0]; } else { [albumArr addObject:[self modelWithResult:fetchResult name:collection.localizedTitle isCameraRoll:NO needFetchAssets:needFetchAssets]]; } } } if (completion && albumArr.count > 0) completion(albumArr); } else { [self.assetLibrary enumerateGroupsWithTypes:ALAssetsGroupAll usingBlock:^(ALAssetsGroup *group, BOOL *stop) { if (group == nil) { if (completion && albumArr.count > 0) completion(albumArr); } if ([group numberOfAssets] < 1) return; NSString *name = [group valueForProperty:ALAssetsGroupPropertyName]; if ([self.pickerDelegate respondsToSelector:@selector(isAlbumCanSelect:result:)]) { if (![self.pickerDelegate isAlbumCanSelect:name result:group]) { return; } } if ([self isCameraRollAlbum:group]) { [albumArr insertObject:[self modelWithResult:group name:name isCameraRoll:YES needFetchAssets:needFetchAssets] atIndex:0]; } else if ([[group valueForProperty:ALAssetsGroupPropertyType] intValue] == ALAssetsGroupPhotoStream) { if (albumArr.count) { [albumArr insertObject:[self modelWithResult:group name:name isCameraRoll:NO needFetchAssets:needFetchAssets] atIndex:1]; } else { [albumArr addObject:[self modelWithResult:group name:name isCameraRoll:NO needFetchAssets:needFetchAssets]]; } } else { [albumArr addObject:[self modelWithResult:group name:name isCameraRoll:NO needFetchAssets:needFetchAssets]]; } } failureBlock:nil]; } } #pragma mark - Get Assets /// Get Assets 获得照片数组 - (void)getAssetsFromFetchResult:(id)result completion:(void (^)(NSArray *))completion { TZImagePickerConfig *config = [TZImagePickerConfig sharedInstance]; return [self getAssetsFromFetchResult:result allowPickingVideo:config.allowPickingVideo allowPickingImage:config.allowPickingImage completion:completion]; } - (void)getAssetsFromFetchResult:(id)result allowPickingVideo:(BOOL)allowPickingVideo allowPickingImage:(BOOL)allowPickingImage completion:(void (^)(NSArray *))completion { NSMutableArray *photoArr = [NSMutableArray array]; if ([result isKindOfClass:[PHFetchResult class]]) { PHFetchResult *fetchResult = (PHFetchResult *)result; [fetchResult enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { TZAssetModel *model = [self assetModelWithAsset:obj allowPickingVideo:allowPickingVideo allowPickingImage:allowPickingImage]; if (model) { [photoArr addObject:model]; } }]; if (completion) completion(photoArr); } else if ([result isKindOfClass:[ALAssetsGroup class]]) { ALAssetsGroup *group = (ALAssetsGroup *)result; if (allowPickingImage && allowPickingVideo) { [group setAssetsFilter:[ALAssetsFilter allAssets]]; } else if (allowPickingVideo) { [group setAssetsFilter:[ALAssetsFilter allVideos]]; } else if (allowPickingImage) { [group setAssetsFilter:[ALAssetsFilter allPhotos]]; } ALAssetsGroupEnumerationResultsBlock resultBlock = ^(ALAsset *result, NSUInteger index, BOOL *stop) { if (result == nil) { if (completion) completion(photoArr); } TZAssetModel *model = [self assetModelWithAsset:result allowPickingVideo:allowPickingVideo allowPickingImage:allowPickingImage]; if (model) { [photoArr addObject:model]; } }; if (self.sortAscendingByModificationDate) { [group enumerateAssetsUsingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop) { if (resultBlock) { resultBlock(result,index,stop); } }]; } else { [group enumerateAssetsWithOptions:NSEnumerationReverse usingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop) { if (resultBlock) { resultBlock(result,index,stop); } }]; } } } /// Get asset at index 获得下标为index的单个照片 /// if index beyond bounds, return nil in callback 如果索引越界, 在回调中返回 nil - (void)getAssetFromFetchResult:(id)result atIndex:(NSInteger)index allowPickingVideo:(BOOL)allowPickingVideo allowPickingImage:(BOOL)allowPickingImage completion:(void (^)(TZAssetModel *))completion { if ([result isKindOfClass:[PHFetchResult class]]) { PHFetchResult *fetchResult = (PHFetchResult *)result; PHAsset *asset; @try { asset = fetchResult[index]; } @catch (NSException* e) { if (completion) completion(nil); return; } TZAssetModel *model = [self assetModelWithAsset:asset allowPickingVideo:allowPickingVideo allowPickingImage:allowPickingImage]; if (completion) completion(model); } else if ([result isKindOfClass:[ALAssetsGroup class]]) { ALAssetsGroup *group = (ALAssetsGroup *)result; if (allowPickingImage && allowPickingVideo) { [group setAssetsFilter:[ALAssetsFilter allAssets]]; } else if (allowPickingVideo) { [group setAssetsFilter:[ALAssetsFilter allVideos]]; } else if (allowPickingImage) { [group setAssetsFilter:[ALAssetsFilter allPhotos]]; } NSIndexSet *indexSet = [NSIndexSet indexSetWithIndex:index]; @try { [group enumerateAssetsAtIndexes:indexSet options:NSEnumerationConcurrent usingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop) { if (!result) return; TZAssetModel *model = [self assetModelWithAsset:result allowPickingVideo:allowPickingVideo allowPickingImage:allowPickingImage]; if (completion) completion(model); }]; } @catch (NSException* e) { if (completion) completion(nil); } } } - (TZAssetModel *)assetModelWithAsset:(id)asset allowPickingVideo:(BOOL)allowPickingVideo allowPickingImage:(BOOL)allowPickingImage { BOOL canSelect = YES; if ([self.pickerDelegate respondsToSelector:@selector(isAssetCanSelect:)]) { canSelect = [self.pickerDelegate isAssetCanSelect:asset]; } if (!canSelect) return nil; TZAssetModel *model; TZAssetModelMediaType type = [self getAssetType:asset]; if ([asset isKindOfClass:[PHAsset class]]) { if (!allowPickingVideo && type == TZAssetModelMediaTypeVideo) return nil; if (!allowPickingImage && type == TZAssetModelMediaTypePhoto) return nil; if (!allowPickingImage && type == TZAssetModelMediaTypePhotoGif) return nil; PHAsset *phAsset = (PHAsset *)asset; if (self.hideWhenCanNotSelect) { // 过滤掉尺寸不满足要求的图片 if (![self isPhotoSelectableWithAsset:phAsset]) { return nil; } } NSString *timeLength = type == TZAssetModelMediaTypeVideo ? [NSString stringWithFormat:@"%0.0f",phAsset.duration] : @""; timeLength = [self getNewTimeFromDurationSecond:timeLength.integerValue]; model = [TZAssetModel modelWithAsset:asset type:type timeLength:timeLength]; } else { if (!allowPickingVideo){ model = [TZAssetModel modelWithAsset:asset type:type]; return model; } /// Allow picking video if (type == TZAssetModelMediaTypeVideo) { NSTimeInterval duration = [[asset valueForProperty:ALAssetPropertyDuration] doubleValue]; NSString *timeLength = [NSString stringWithFormat:@"%0.0f",duration]; timeLength = [self getNewTimeFromDurationSecond:timeLength.integerValue]; model = [TZAssetModel modelWithAsset:asset type:type timeLength:timeLength]; } else { if (self.hideWhenCanNotSelect) { // 过滤掉尺寸不满足要求的图片 if (![self isPhotoSelectableWithAsset:asset]) { return nil; } } model = [TZAssetModel modelWithAsset:asset type:type]; } } return model; } - (TZAssetModelMediaType)getAssetType:(id)asset { TZAssetModelMediaType type = TZAssetModelMediaTypePhoto; if ([asset isKindOfClass:[PHAsset class]]) { PHAsset *phAsset = (PHAsset *)asset; if (phAsset.mediaType == PHAssetMediaTypeVideo) type = TZAssetModelMediaTypeVideo; else if (phAsset.mediaType == PHAssetMediaTypeAudio) type = TZAssetModelMediaTypeAudio; else if (phAsset.mediaType == PHAssetMediaTypeImage) { if (iOS9_1Later) { // if (asset.mediaSubtypes == PHAssetMediaSubtypePhotoLive) type = TZAssetModelMediaTypeLivePhoto; } // Gif if ([[phAsset valueForKey:@"filename"] hasSuffix:@"GIF"]) { type = TZAssetModelMediaTypePhotoGif; } } } else { if ([[asset valueForProperty:ALAssetPropertyType] isEqualToString:ALAssetTypeVideo]) { type = TZAssetModelMediaTypeVideo; } } return type; } - (NSString *)getNewTimeFromDurationSecond:(NSInteger)duration { NSString *newTime; if (duration < 10) { newTime = [NSString stringWithFormat:@"0:0%zd",duration]; } else if (duration < 60) { newTime = [NSString stringWithFormat:@"0:%zd",duration]; } else { NSInteger min = duration / 60; NSInteger sec = duration - (min * 60); if (sec < 10) { newTime = [NSString stringWithFormat:@"%zd:0%zd",min,sec]; } else { newTime = [NSString stringWithFormat:@"%zd:%zd",min,sec]; } } return newTime; } /// Get photo bytes 获得一组照片的大小 - (void)getPhotosBytesWithArray:(NSArray *)photos completion:(void (^)(NSString *totalBytes))completion { if (!photos || !photos.count) { if (completion) completion(@"0B"); return; } __block NSInteger dataLength = 0; __block NSInteger assetCount = 0; for (NSInteger i = 0; i < photos.count; i++) { TZAssetModel *model = photos[i]; if ([model.asset isKindOfClass:[PHAsset class]]) { PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init]; options.resizeMode = PHImageRequestOptionsResizeModeFast; options.networkAccessAllowed = YES; [[PHImageManager defaultManager] requestImageDataForAsset:model.asset options:options resultHandler:^(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info) { if (model.type != TZAssetModelMediaTypeVideo) dataLength += imageData.length; assetCount ++; if (assetCount >= photos.count) { NSString *bytes = [self getBytesFromDataLength:dataLength]; if (completion) completion(bytes); } }]; } else if ([model.asset isKindOfClass:[ALAsset class]]) { ALAssetRepresentation *representation = [model.asset defaultRepresentation]; if (model.type != TZAssetModelMediaTypeVideo) dataLength += (NSInteger)representation.size; if (i >= photos.count - 1) { NSString *bytes = [self getBytesFromDataLength:dataLength]; if (completion) completion(bytes); } } } } - (NSString *)getBytesFromDataLength:(NSInteger)dataLength { NSString *bytes; if (dataLength >= 0.1 * (1024 * 1024)) { bytes = [NSString stringWithFormat:@"%0.1fM",dataLength/1024/1024.0]; } else if (dataLength >= 1024) { bytes = [NSString stringWithFormat:@"%0.0fK",dataLength/1024.0]; } else { bytes = [NSString stringWithFormat:@"%zdB",dataLength]; } return bytes; } #pragma mark - Get Photo /// Get photo 获得照片本身 - (int32_t)getPhotoWithAsset:(id)asset completion:(void (^)(UIImage *, NSDictionary *, BOOL isDegraded))completion { CGFloat fullScreenWidth = TZScreenWidth; if (fullScreenWidth > _photoPreviewMaxWidth) { fullScreenWidth = _photoPreviewMaxWidth; } return [self getPhotoWithAsset:asset photoWidth:fullScreenWidth completion:completion progressHandler:nil networkAccessAllowed:YES]; } - (int32_t)getPhotoWithAsset:(id)asset photoWidth:(CGFloat)photoWidth completion:(void (^)(UIImage *photo,NSDictionary *info,BOOL isDegraded))completion { return [self getPhotoWithAsset:asset photoWidth:photoWidth completion:completion progressHandler:nil networkAccessAllowed:YES]; } - (int32_t)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 { CGFloat fullScreenWidth = TZScreenWidth; if (fullScreenWidth > _photoPreviewMaxWidth) { fullScreenWidth = _photoPreviewMaxWidth; } return [self getPhotoWithAsset:asset photoWidth:fullScreenWidth completion:completion progressHandler:progressHandler networkAccessAllowed:networkAccessAllowed]; } - (int32_t)requestImageDataForAsset:(id)asset completion:(void (^)(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info))completion progressHandler:(void (^)(double progress, NSError *error, BOOL *stop, NSDictionary *info))progressHandler { if ([asset isKindOfClass:[PHAsset class]]) { PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init]; options.progressHandler = ^(double progress, NSError *error, BOOL *stop, NSDictionary *info) { dispatch_async(dispatch_get_main_queue(), ^{ if (progressHandler) { progressHandler(progress, error, stop, info); } }); }; options.networkAccessAllowed = YES; options.resizeMode = PHImageRequestOptionsResizeModeFast; int32_t imageRequestID = [[PHImageManager defaultManager] requestImageDataForAsset:asset options:options resultHandler:^(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info) { if (completion) completion(imageData,dataUTI,orientation,info); }]; return imageRequestID; } else if ([asset isKindOfClass:[ALAsset class]]) { ALAsset *alAsset = (ALAsset *)asset; dispatch_async(dispatch_get_global_queue(0,0), ^{ ALAssetRepresentation *assetRep = [alAsset defaultRepresentation]; CGImageRef fullScrennImageRef = [assetRep fullScreenImage]; UIImage *fullScrennImage = [UIImage imageWithCGImage:fullScrennImageRef scale:2.0 orientation:UIImageOrientationUp]; dispatch_async(dispatch_get_main_queue(), ^{ if (completion) completion(UIImageJPEGRepresentation(fullScrennImage, 0.83), nil, UIImageOrientationUp, nil); }); }); } return 0; } - (int32_t)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 { if ([asset isKindOfClass:[PHAsset class]]) { CGSize imageSize; if (photoWidth < TZScreenWidth && photoWidth < _photoPreviewMaxWidth) { imageSize = AssetGridThumbnailSize; } else { PHAsset *phAsset = (PHAsset *)asset; CGFloat aspectRatio = phAsset.pixelWidth / (CGFloat)phAsset.pixelHeight; CGFloat pixelWidth = photoWidth * TZScreenScale * 1.5; // 超宽图片 if (aspectRatio > 1.8) { pixelWidth = pixelWidth * aspectRatio; } // 超高图片 if (aspectRatio < 0.2) { pixelWidth = pixelWidth * 0.5; } CGFloat pixelHeight = pixelWidth / aspectRatio; imageSize = CGSizeMake(pixelWidth, pixelHeight); } __block UIImage *image; // 修复获取图片时出现的瞬间内存过高问题 // 下面两行代码,来自hsjcom,他的github是:https://github.com/hsjcom 表示感谢 PHImageRequestOptions *option = [[PHImageRequestOptions alloc] init]; option.resizeMode = PHImageRequestOptionsResizeModeFast; int32_t imageRequestID = [[PHImageManager defaultManager] requestImageForAsset:asset targetSize:imageSize contentMode:PHImageContentModeAspectFill options:option resultHandler:^(UIImage *result, NSDictionary *info) { if (result) { image = result; } BOOL downloadFinined = (![[info objectForKey:PHImageCancelledKey] boolValue] && ![info objectForKey:PHImageErrorKey]); if (downloadFinined && result) { result = [self fixOrientation:result]; if (completion) completion(result,info,[[info objectForKey:PHImageResultIsDegradedKey] boolValue]); } // Download image from iCloud / 从iCloud下载图片 if ([info objectForKey:PHImageResultIsInCloudKey] && !result && networkAccessAllowed) { PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init]; options.progressHandler = ^(double progress, NSError *error, BOOL *stop, NSDictionary *info) { dispatch_async(dispatch_get_main_queue(), ^{ if (progressHandler) { progressHandler(progress, error, stop, info); } }); }; options.networkAccessAllowed = YES; options.resizeMode = PHImageRequestOptionsResizeModeFast; [[PHImageManager defaultManager] requestImageDataForAsset:asset options:options resultHandler:^(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info) { UIImage *resultImage = [UIImage imageWithData:imageData scale:0.1]; resultImage = [self scaleImage:resultImage toSize:imageSize]; if (!resultImage) { resultImage = image; } resultImage = [self fixOrientation:resultImage]; if (completion) completion(resultImage,info,NO); }]; } }]; return imageRequestID; } else if ([asset isKindOfClass:[ALAsset class]]) { ALAsset *alAsset = (ALAsset *)asset; dispatch_async(dispatch_get_global_queue(0,0), ^{ CGImageRef thumbnailImageRef = alAsset.thumbnail; UIImage *thumbnailImage = [UIImage imageWithCGImage:thumbnailImageRef scale:2.0 orientation:UIImageOrientationUp]; dispatch_async(dispatch_get_main_queue(), ^{ if (completion) completion(thumbnailImage,nil,YES); if (photoWidth == TZScreenWidth || photoWidth == self->_photoPreviewMaxWidth) { dispatch_async(dispatch_get_global_queue(0,0), ^{ ALAssetRepresentation *assetRep = [alAsset defaultRepresentation]; CGImageRef fullScrennImageRef = [assetRep fullScreenImage]; UIImage *fullScrennImage = [UIImage imageWithCGImage:fullScrennImageRef scale:2.0 orientation:UIImageOrientationUp]; dispatch_async(dispatch_get_main_queue(), ^{ if (completion) completion(fullScrennImage,nil,NO); }); }); } }); }); } return 0; } /// Get postImage / 获取封面图 - (void)getPostImageWithAlbumModel:(TZAlbumModel *)model completion:(void (^)(UIImage *))completion { if (iOS8Later) { id asset = [model.result lastObject]; if (!self.sortAscendingByModificationDate) { asset = [model.result firstObject]; } [[TZImageManager manager] getPhotoWithAsset:asset photoWidth:80 completion:^(UIImage *photo, NSDictionary *info, BOOL isDegraded) { if (completion) completion(photo); }]; } else { ALAssetsGroup *group = model.result; UIImage *postImage = [UIImage imageWithCGImage:group.posterImage]; if (completion) completion(postImage); } } /// Get Original Photo / 获取原图 - (void)getOriginalPhotoWithAsset:(id)asset completion:(void (^)(UIImage *photo,NSDictionary *info))completion { [self getOriginalPhotoWithAsset:asset newCompletion:^(UIImage *photo, NSDictionary *info, BOOL isDegraded) { if (completion) { completion(photo,info); } }]; } - (void)getOriginalPhotoWithAsset:(id)asset newCompletion:(void (^)(UIImage *photo,NSDictionary *info,BOOL isDegraded))completion { if ([asset isKindOfClass:[PHAsset class]]) { PHImageRequestOptions *option = [[PHImageRequestOptions alloc]init]; option.networkAccessAllowed = YES; option.resizeMode = PHImageRequestOptionsResizeModeFast; [[PHImageManager defaultManager] requestImageForAsset:asset targetSize:PHImageManagerMaximumSize contentMode:PHImageContentModeAspectFit options:option resultHandler:^(UIImage *result, NSDictionary *info) { BOOL downloadFinined = (![[info objectForKey:PHImageCancelledKey] boolValue] && ![info objectForKey:PHImageErrorKey]); if (downloadFinined && result) { result = [self fixOrientation:result]; BOOL isDegraded = [[info objectForKey:PHImageResultIsDegradedKey] boolValue]; if (completion) completion(result,info,isDegraded); } }]; } else if ([asset isKindOfClass:[ALAsset class]]) { ALAsset *alAsset = (ALAsset *)asset; ALAssetRepresentation *assetRep = [alAsset defaultRepresentation]; dispatch_async(dispatch_get_global_queue(0,0), ^{ CGImageRef originalImageRef = [assetRep fullResolutionImage]; UIImage *originalImage = [UIImage imageWithCGImage:originalImageRef scale:1.0 orientation:UIImageOrientationUp]; dispatch_async(dispatch_get_main_queue(), ^{ if (completion) completion(originalImage,nil,NO); }); }); } } - (void)getOriginalPhotoDataWithAsset:(id)asset completion:(void (^)(NSData *data,NSDictionary *info,BOOL isDegraded))completion { if ([asset isKindOfClass:[PHAsset class]]) { PHImageRequestOptions *option = [[PHImageRequestOptions alloc] init]; option.networkAccessAllowed = YES; if ([[asset valueForKey:@"filename"] hasSuffix:@"GIF"]) { // if version isn't PHImageRequestOptionsVersionOriginal, the gif may cann't play option.version = PHImageRequestOptionsVersionOriginal; } option.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat; [[PHImageManager defaultManager] requestImageDataForAsset:asset options:option resultHandler:^(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info) { BOOL downloadFinined = (![[info objectForKey:PHImageCancelledKey] boolValue] && ![info objectForKey:PHImageErrorKey]); if (downloadFinined && imageData) { if (completion) completion(imageData,info,NO); } }]; } else if ([asset isKindOfClass:[ALAsset class]]) { ALAsset *alAsset = (ALAsset *)asset; ALAssetRepresentation *assetRep = [alAsset defaultRepresentation]; Byte *imageBuffer = (Byte *)malloc(assetRep.size); NSUInteger bufferSize = [assetRep getBytes:imageBuffer fromOffset:0.0 length:assetRep.size error:nil]; NSData *imageData = [NSData dataWithBytesNoCopy:imageBuffer length:bufferSize freeWhenDone:YES]; if (completion) completion(imageData,nil,NO); } } #pragma mark - Save photo - (void)savePhotoWithImage:(UIImage *)image completion:(void (^)(NSError *error))completion { [self savePhotoWithImage:image location:nil completion:completion]; } - (void)savePhotoWithImage:(UIImage *)image location:(CLLocation *)location completion:(void (^)(NSError *error))completion { if (iOS8Later) { [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{ if (iOS9Later) { NSData *data = UIImageJPEGRepresentation(image, 0.9); PHAssetResourceCreationOptions *options = [[PHAssetResourceCreationOptions alloc] init]; options.shouldMoveFile = YES; PHAssetCreationRequest *request = [PHAssetCreationRequest creationRequestForAsset]; [request addResourceWithType:PHAssetResourceTypePhoto data:data options:options]; if (location) { request.location = location; } request.creationDate = [NSDate date]; } else { PHAssetChangeRequest *request = [PHAssetChangeRequest creationRequestForAssetFromImage:image]; if (location) { request.location = location; } request.creationDate = [NSDate date]; } } completionHandler:^(BOOL success, NSError *error) { dispatch_async(dispatch_get_main_queue(), ^{ if (success && completion) { completion(nil); } else if (error) { NSLog(@"保存照片出错:%@",error.localizedDescription); if (completion) { completion(error); } } }); }]; } else { [self.assetLibrary writeImageToSavedPhotosAlbum:image.CGImage orientation:[self orientationFromImage:image] completionBlock:^(NSURL *assetURL, NSError *error) { if (error) { NSLog(@"保存图片失败:%@",error.localizedDescription); if (completion) { completion(error); } } else { // 多给系统0.5秒的时间,让系统去更新相册数据 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ if (completion) { completion(nil); } }); } }]; } } #pragma mark - Save video - (void)saveVideoWithUrl:(NSURL *)url completion:(void (^)(NSError *error))completion { [self saveVideoWithUrl:url location:nil completion:completion]; } - (void)saveVideoWithUrl:(NSURL *)url location:(CLLocation *)location completion:(void (^)(NSError *error))completion { if (iOS8Later) { [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{ if (iOS9Later) { PHAssetResourceCreationOptions *options = [[PHAssetResourceCreationOptions alloc] init]; options.shouldMoveFile = YES; PHAssetCreationRequest *request = [PHAssetCreationRequest creationRequestForAsset]; [request addResourceWithType:PHAssetResourceTypeVideo fileURL:url options:options]; if (location) { request.location = location; } request.creationDate = [NSDate date]; } else { PHAssetChangeRequest *request = [PHAssetChangeRequest creationRequestForAssetFromVideoAtFileURL:url]; if (location) { request.location = location; } request.creationDate = [NSDate date]; } } completionHandler:^(BOOL success, NSError *error) { dispatch_async(dispatch_get_main_queue(), ^{ if (success && completion) { completion(nil); } else if (error) { NSLog(@"保存视频出错:%@",error.localizedDescription); if (completion) { completion(error); } } }); }]; } else { [self.assetLibrary writeVideoAtPathToSavedPhotosAlbum:url completionBlock:^(NSURL *assetURL, NSError *error) { if (error) { NSLog(@"保存视频出错:%@",error.localizedDescription); if (completion) { completion(error); } } else { dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ if (completion) { completion(nil); } }); } }]; } } #pragma mark - Get Video /// Get Video / 获取视频 - (void)getVideoWithAsset:(id)asset completion:(void (^)(AVPlayerItem *, NSDictionary *))completion { [self getVideoWithAsset:asset progressHandler:nil completion:completion]; } - (void)getVideoWithAsset:(id)asset progressHandler:(void (^)(double progress, NSError *error, BOOL *stop, NSDictionary *info))progressHandler completion:(void (^)(AVPlayerItem *, NSDictionary *))completion { if ([asset isKindOfClass:[PHAsset class]]) { PHVideoRequestOptions *option = [[PHVideoRequestOptions alloc] init]; option.networkAccessAllowed = YES; option.progressHandler = ^(double progress, NSError *error, BOOL *stop, NSDictionary *info) { dispatch_async(dispatch_get_main_queue(), ^{ if (progressHandler) { progressHandler(progress, error, stop, info); } }); }; [[PHImageManager defaultManager] requestPlayerItemForVideo:asset options:option resultHandler:^(AVPlayerItem *playerItem, NSDictionary *info) { if (completion) completion(playerItem,info); }]; } else if ([asset isKindOfClass:[ALAsset class]]) { ALAsset *alAsset = (ALAsset *)asset; ALAssetRepresentation *defaultRepresentation = [alAsset defaultRepresentation]; NSString *uti = [defaultRepresentation UTI]; NSURL *videoURL = [[asset valueForProperty:ALAssetPropertyURLs] valueForKey:uti]; AVPlayerItem *playerItem = [[AVPlayerItem alloc] initWithURL:videoURL]; if (completion && playerItem) completion(playerItem,nil); } } #pragma mark - Export video /// Export Video / 导出视频 - (void)getVideoOutputPathWithAsset:(id)asset success:(void (^)(NSString *outputPath))success failure:(void (^)(NSString *errorMessage, NSError *error))failure { [self getVideoOutputPathWithAsset:asset presetName:AVAssetExportPreset640x480 success:success failure:failure]; } - (void)getVideoOutputPathWithAsset:(id)asset presetName:(NSString *)presetName success:(void (^)(NSString *outputPath))success failure:(void (^)(NSString *errorMessage, NSError *error))failure { if ([asset isKindOfClass:[PHAsset class]]) { PHVideoRequestOptions* options = [[PHVideoRequestOptions alloc] init]; options.version = PHVideoRequestOptionsVersionOriginal; options.deliveryMode = PHVideoRequestOptionsDeliveryModeAutomatic; options.networkAccessAllowed = YES; [[PHImageManager defaultManager] requestAVAssetForVideo:asset options:options resultHandler:^(AVAsset* avasset, AVAudioMix* audioMix, NSDictionary* info){ // NSLog(@"Info:\n%@",info); AVURLAsset *videoAsset = (AVURLAsset*)avasset; // NSLog(@"AVAsset URL: %@",myAsset.URL); [self startExportVideoWithVideoAsset:videoAsset presetName:presetName success:success failure:failure]; }]; } else if ([asset isKindOfClass:[ALAsset class]]) { NSURL *videoURL =[asset valueForProperty:ALAssetPropertyAssetURL]; // ALAssetPropertyURLs AVURLAsset *videoAsset = [[AVURLAsset alloc] initWithURL:videoURL options:nil]; [self startExportVideoWithVideoAsset:videoAsset presetName:presetName success:success failure:failure]; } } /// Deprecated, Use -getVideoOutputPathWithAsset:failure:success: - (void)getVideoOutputPathWithAsset:(id)asset completion:(void (^)(NSString *outputPath))completion { [self getVideoOutputPathWithAsset:asset success:completion failure:nil]; } - (void)startExportVideoWithVideoAsset:(AVURLAsset *)videoAsset presetName:(NSString *)presetName success:(void (^)(NSString *outputPath))success failure:(void (^)(NSString *errorMessage, NSError *error))failure { // Find compatible presets by video asset. NSArray *presets = [AVAssetExportSession exportPresetsCompatibleWithAsset:videoAsset]; // Begin to compress video // Now we just compress to low resolution if it supports // If you need to upload to the server, but server does't support to upload by streaming, // You can compress the resolution to lower. Or you can support more higher resolution. if ([presets containsObject:presetName]) { AVAssetExportSession *session = [[AVAssetExportSession alloc] initWithAsset:videoAsset presetName:presetName]; NSDateFormatter *formater = [[NSDateFormatter alloc] init]; [formater setDateFormat:@"yyyy-MM-dd-HH:mm:ss-SSS"]; NSString *outputPath = [NSHomeDirectory() stringByAppendingFormat:@"/tmp/output-%@.mp4", [formater stringFromDate:[NSDate date]]]; // NSLog(@"video outputPath = %@",outputPath); session.outputURL = [NSURL fileURLWithPath:outputPath]; // Optimize for network use. session.shouldOptimizeForNetworkUse = true; NSArray *supportedTypeArray = session.supportedFileTypes; if ([supportedTypeArray containsObject:AVFileTypeMPEG4]) { session.outputFileType = AVFileTypeMPEG4; } else if (supportedTypeArray.count == 0) { if (failure) { failure(@"该视频类型暂不支持导出", nil); } NSLog(@"No supported file types 视频类型暂不支持导出"); return; } else { session.outputFileType = [supportedTypeArray objectAtIndex:0]; } if (![[NSFileManager defaultManager] fileExistsAtPath:[NSHomeDirectory() stringByAppendingFormat:@"/tmp"]]) { [[NSFileManager defaultManager] createDirectoryAtPath:[NSHomeDirectory() stringByAppendingFormat:@"/tmp"] withIntermediateDirectories:YES attributes:nil error:nil]; } AVMutableVideoComposition *videoComposition = [self fixedCompositionWithAsset:videoAsset]; if (videoComposition.renderSize.width) { // 修正视频转向 session.videoComposition = videoComposition; } // Begin to export video to the output path asynchronously. [session exportAsynchronouslyWithCompletionHandler:^(void) { dispatch_async(dispatch_get_main_queue(), ^{ switch (session.status) { case AVAssetExportSessionStatusUnknown: { NSLog(@"AVAssetExportSessionStatusUnknown"); } break; case AVAssetExportSessionStatusWaiting: { NSLog(@"AVAssetExportSessionStatusWaiting"); } break; case AVAssetExportSessionStatusExporting: { NSLog(@"AVAssetExportSessionStatusExporting"); } break; case AVAssetExportSessionStatusCompleted: { NSLog(@"AVAssetExportSessionStatusCompleted"); if (success) { success(outputPath); } } break; case AVAssetExportSessionStatusFailed: { NSLog(@"AVAssetExportSessionStatusFailed"); if (failure) { failure(@"视频导出失败", session.error); } } break; case AVAssetExportSessionStatusCancelled: { NSLog(@"AVAssetExportSessionStatusCancelled"); if (failure) { failure(@"导出任务已被取消", nil); } } break; default: break; } }); }]; } else { if (failure) { NSString *errorMessage = [NSString stringWithFormat:@"当前设备不支持该预设:%@", presetName]; failure(errorMessage, nil); } } } /// Judge is a assets array contain the asset 判断一个assets数组是否包含这个asset - (BOOL)isAssetsArray:(NSArray *)assets containAsset:(id)asset { if (iOS8Later) { return [assets containsObject:asset]; } else { NSMutableArray *selectedAssetUrls = [NSMutableArray array]; for (ALAsset *asset_item in assets) { [selectedAssetUrls addObject:[asset_item valueForProperty:ALAssetPropertyURLs]]; } return [selectedAssetUrls containsObject:[asset valueForProperty:ALAssetPropertyURLs]]; } } - (BOOL)isCameraRollAlbum:(id)metadata { if ([metadata isKindOfClass:[PHAssetCollection class]]) { NSString *versionStr = [[UIDevice currentDevice].systemVersion stringByReplacingOccurrencesOfString:@"." withString:@""]; if (versionStr.length <= 1) { versionStr = [versionStr stringByAppendingString:@"00"]; } else if (versionStr.length <= 2) { versionStr = [versionStr stringByAppendingString:@"0"]; } CGFloat version = versionStr.floatValue; // 目前已知8.0.0 ~ 8.0.2系统,拍照后的图片会保存在最近添加中 if (version >= 800 && version <= 802) { return ((PHAssetCollection *)metadata).assetCollectionSubtype == PHAssetCollectionSubtypeSmartAlbumRecentlyAdded; } else { return ((PHAssetCollection *)metadata).assetCollectionSubtype == PHAssetCollectionSubtypeSmartAlbumUserLibrary; } } if ([metadata isKindOfClass:[ALAssetsGroup class]]) { ALAssetsGroup *group = metadata; return ([[group valueForProperty:ALAssetsGroupPropertyType] intValue] == ALAssetsGroupSavedPhotos); } return NO; } - (NSString *)getAssetIdentifier:(id)asset { if (iOS8Later) { PHAsset *phAsset = (PHAsset *)asset; return phAsset.localIdentifier; } else { ALAsset *alAsset = (ALAsset *)asset; NSURL *assetUrl = [alAsset valueForProperty:ALAssetPropertyAssetURL]; return assetUrl.absoluteString; } } /// 检查照片大小是否满足最小要求 - (BOOL)isPhotoSelectableWithAsset:(id)asset { CGSize photoSize = [self photoSizeWithAsset:asset]; if (self.minPhotoWidthSelectable > photoSize.width || self.minPhotoHeightSelectable > photoSize.height) { return NO; } return YES; } - (CGSize)photoSizeWithAsset:(id)asset { if (iOS8Later) { PHAsset *phAsset = (PHAsset *)asset; return CGSizeMake(phAsset.pixelWidth, phAsset.pixelHeight); } else { ALAsset *alAsset = (ALAsset *)asset; return alAsset.defaultRepresentation.dimensions; } } #pragma mark - Private Method - (TZAlbumModel *)modelWithResult:(id)result name:(NSString *)name isCameraRoll:(BOOL)isCameraRoll needFetchAssets:(BOOL)needFetchAssets { TZAlbumModel *model = [[TZAlbumModel alloc] init]; [model setResult:result needFetchAssets:needFetchAssets]; model.name = name; model.isCameraRoll = isCameraRoll; if ([result isKindOfClass:[PHFetchResult class]]) { PHFetchResult *fetchResult = (PHFetchResult *)result; model.count = fetchResult.count; } else if ([result isKindOfClass:[ALAssetsGroup class]]) { ALAssetsGroup *group = (ALAssetsGroup *)result; model.count = [group numberOfAssets]; } return model; } /// 缩放图片至新尺寸 - (UIImage *)scaleImage:(UIImage *)image toSize:(CGSize)size { if (image.size.width > size.width) { UIGraphicsBeginImageContext(size); [image drawInRect:CGRectMake(0, 0, size.width, size.height)]; UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return newImage; /* 好像不怎么管用:https://mp.weixin.qq.com/s/CiqMlEIp1Ir2EJSDGgMooQ CGFloat maxPixelSize = MAX(size.width, size.height); CGImageSourceRef sourceRef = CGImageSourceCreateWithData((__bridge CFDataRef)UIImageJPEGRepresentation(image, 0.9), nil); NSDictionary *options = @{(__bridge id)kCGImageSourceCreateThumbnailFromImageAlways:(__bridge id)kCFBooleanTrue, (__bridge id)kCGImageSourceThumbnailMaxPixelSize:[NSNumber numberWithFloat:maxPixelSize] }; CGImageRef imageRef = CGImageSourceCreateImageAtIndex(sourceRef, 0, (__bridge CFDictionaryRef)options); UIImage *newImage = [UIImage imageWithCGImage:imageRef scale:2 orientation:image.imageOrientation]; CGImageRelease(imageRef); CFRelease(sourceRef); return newImage; */ } else { return image; } } - (ALAssetOrientation)orientationFromImage:(UIImage *)image { NSInteger orientation = image.imageOrientation; return orientation; } /// 获取优化后的视频转向信息 - (AVMutableVideoComposition *)fixedCompositionWithAsset:(AVAsset *)videoAsset { AVMutableVideoComposition *videoComposition = [AVMutableVideoComposition videoComposition]; // 视频转向 int degrees = [self degressFromVideoFileWithAsset:videoAsset]; if (degrees != 0) { CGAffineTransform translateToCenter; CGAffineTransform mixedTransform; videoComposition.frameDuration = CMTimeMake(1, 30); NSArray *tracks = [videoAsset tracksWithMediaType:AVMediaTypeVideo]; AVAssetTrack *videoTrack = [tracks objectAtIndex:0]; AVMutableVideoCompositionInstruction *roateInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction]; roateInstruction.timeRange = CMTimeRangeMake(kCMTimeZero, [videoAsset duration]); AVMutableVideoCompositionLayerInstruction *roateLayerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:videoTrack]; if (degrees == 90) { // 顺时针旋转90° translateToCenter = CGAffineTransformMakeTranslation(videoTrack.naturalSize.height, 0.0); mixedTransform = CGAffineTransformRotate(translateToCenter,M_PI_2); videoComposition.renderSize = CGSizeMake(videoTrack.naturalSize.height,videoTrack.naturalSize.width); [roateLayerInstruction setTransform:mixedTransform atTime:kCMTimeZero]; } else if(degrees == 180){ // 顺时针旋转180° translateToCenter = CGAffineTransformMakeTranslation(videoTrack.naturalSize.width, videoTrack.naturalSize.height); mixedTransform = CGAffineTransformRotate(translateToCenter,M_PI); videoComposition.renderSize = CGSizeMake(videoTrack.naturalSize.width,videoTrack.naturalSize.height); [roateLayerInstruction setTransform:mixedTransform atTime:kCMTimeZero]; } else if(degrees == 270){ // 顺时针旋转270° translateToCenter = CGAffineTransformMakeTranslation(0.0, videoTrack.naturalSize.width); mixedTransform = CGAffineTransformRotate(translateToCenter,M_PI_2*3.0); videoComposition.renderSize = CGSizeMake(videoTrack.naturalSize.height,videoTrack.naturalSize.width); [roateLayerInstruction setTransform:mixedTransform atTime:kCMTimeZero]; } roateInstruction.layerInstructions = @[roateLayerInstruction]; // 加入视频方向信息 videoComposition.instructions = @[roateInstruction]; } return videoComposition; } /// 获取视频角度 - (int)degressFromVideoFileWithAsset:(AVAsset *)asset { int degress = 0; NSArray *tracks = [asset tracksWithMediaType:AVMediaTypeVideo]; if([tracks count] > 0) { AVAssetTrack *videoTrack = [tracks objectAtIndex:0]; CGAffineTransform t = videoTrack.preferredTransform; if(t.a == 0 && t.b == 1.0 && t.c == -1.0 && t.d == 0){ // Portrait degress = 90; } else if(t.a == 0 && t.b == -1.0 && t.c == 1.0 && t.d == 0){ // PortraitUpsideDown degress = 270; } else if(t.a == 1.0 && t.b == 0 && t.c == 0 && t.d == 1.0){ // LandscapeRight degress = 0; } else if(t.a == -1.0 && t.b == 0 && t.c == 0 && t.d == -1.0){ // LandscapeLeft degress = 180; } } return degress; } /// 修正图片转向 - (UIImage *)fixOrientation:(UIImage *)aImage { if (!self.shouldFixOrientation) return aImage; // No-op if the orientation is already correct if (aImage.imageOrientation == UIImageOrientationUp) return aImage; // We need to calculate the proper transformation to make the image upright. // We do it in 2 steps: Rotate if Left/Right/Down, and then flip if Mirrored. CGAffineTransform transform = CGAffineTransformIdentity; switch (aImage.imageOrientation) { case UIImageOrientationDown: case UIImageOrientationDownMirrored: transform = CGAffineTransformTranslate(transform, aImage.size.width, aImage.size.height); transform = CGAffineTransformRotate(transform, M_PI); break; case UIImageOrientationLeft: case UIImageOrientationLeftMirrored: transform = CGAffineTransformTranslate(transform, aImage.size.width, 0); transform = CGAffineTransformRotate(transform, M_PI_2); break; case UIImageOrientationRight: case UIImageOrientationRightMirrored: transform = CGAffineTransformTranslate(transform, 0, aImage.size.height); transform = CGAffineTransformRotate(transform, -M_PI_2); break; default: break; } switch (aImage.imageOrientation) { case UIImageOrientationUpMirrored: case UIImageOrientationDownMirrored: transform = CGAffineTransformTranslate(transform, aImage.size.width, 0); transform = CGAffineTransformScale(transform, -1, 1); break; case UIImageOrientationLeftMirrored: case UIImageOrientationRightMirrored: transform = CGAffineTransformTranslate(transform, aImage.size.height, 0); transform = CGAffineTransformScale(transform, -1, 1); break; default: break; } // Now we draw the underlying CGImage into a new context, applying the transform // calculated above. CGContextRef ctx = CGBitmapContextCreate(NULL, aImage.size.width, aImage.size.height, CGImageGetBitsPerComponent(aImage.CGImage), 0, CGImageGetColorSpace(aImage.CGImage), CGImageGetBitmapInfo(aImage.CGImage)); CGContextConcatCTM(ctx, transform); switch (aImage.imageOrientation) { case UIImageOrientationLeft: case UIImageOrientationLeftMirrored: case UIImageOrientationRight: case UIImageOrientationRightMirrored: // Grr... CGContextDrawImage(ctx, CGRectMake(0,0,aImage.size.height,aImage.size.width), aImage.CGImage); break; default: CGContextDrawImage(ctx, CGRectMake(0,0,aImage.size.width,aImage.size.height), aImage.CGImage); break; } // And now we just create a new UIImage from the drawing context CGImageRef cgimg = CGBitmapContextCreateImage(ctx); UIImage *img = [UIImage imageWithCGImage:cgimg]; CGContextRelease(ctx); CGImageRelease(cgimg); return img; } #pragma clang diagnostic pop @end //@implementation TZSortDescriptor // //- (id)reversedSortDescriptor { // return [NSNumber numberWithBool:![TZImageManager manager].sortAscendingByModificationDate]; //} // //@end