相册 - PhotoKit
一、 PhotoKit的变量:
PHAdjustmentData :
When a user edits an asset, photos saces a PHAdjustmentData object along with the modified image or video data;
在用户编辑一个Asset时, 相册会存储该Asset在修改image或者video数据的过程
PHFetchOptions:
获取资源时的参数, 可以传nil, 即使用系统默认值。
PHFetchResult:
表示一系列的资源结果集合,也可以是相册的集合, 从PHCollection的类方法中获得。
PHImageManager:
用于处理资源的加载, 加载图片的过程带有缓存处理,可以通过传入一个PHImageRequestOptions控制资源的输出尺寸等规格。
PHImageRequestOptions:
控制加载图片时的一系列参数。
PHAsset:
A PHAsset object represent an image or video file that appears in the Photos app, including iCloud Photos content.
一个PHAsset对象代表相册中或者云储存中的一个image或者Video文件;
PHAssetChangeRequest:
you create and use PHAssetChangeRequest objects within a photo library change block to create, delete ,or modify PHAsset objects.
当你在相册中增删改PHAsset对象时需要使用PHAssetChangeRequest对象。
PHAssetCreationRequest:
a PHAssetCreationRequest object , used within a photo library change block, constructs a new photo or video asset from data resources, and adds it to the photos library;
PHAssetCreationRequest对象用于在照片库的增删改查操作,创建一个新的image和video Asset从Data resources中, 然后将其加入Photos Library中;
PHAssetCollectionChangeRequest:
you create and use PHAssetCollectionChangeRequest objects within a photo library change block to create, delete, or modigy PHAssetCollection objects;
当你对photo Library 即 assetCollection进行增删改操作时, 使用PHAssetCollectionChangeRequest对象
PHAssetResourceCreationOptions:
you use a PHAssetResourceCreationOptions abject to specify options when creating a new asset from data resources with a PHAssetCreationRequest Object.
通过PHAssetResourceCreationOptions对象来指定当创建新的Asset的options;
PHAssetResourceManager:
PHAssetResourceManager 对象提供方法访问关联相机Asset资源的基础数据存储
PHAssetCollection:
PHCollection的子类, 表示一个相册或者一个时刻, 或者一个智能相册(系统提供的特定相册)。
PHPhotoLibrary:(重点)
公共的PHPhotoLibrary对象代表用户的相册库,所有的图片和相册管理都要PHPhotoLibrary管理;
PHCollectionList:
表示一组PHCollection, 它本身也是一个PHCollection, 因此PHCollection作为一个集合,可以包含其他集合,这使到PhotoKit的组成比ALAssetsLibrary要复杂一些,

二、PhotoKit的机制
1)、获取资源
PhotoKit 是采用“获取”的方式拉取资源, 这些获取的手段,都是一系列形如class func fetchXXX(..., options: PHFetchOptions) -> PHFetchResult的类方法。具体使用哪个类方法, 则视乎需要获取的是相册、时刻还是西苑, 这类方法中的Option充当了过滤器的作用。可以过滤相册的类型、日期、名称等。从而直接获取对应的资源二不需要枚举;
例如:
// 列出所有相册智能相册
PHFetchResult *smartAlbums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum subtype:PHAssetCollectionSubtypeAlbumRegular options:nil];
// 列出所有用户创建的相册
PHFetchResult *topLevelUserCollections = [PHCollectionList fetchTopLevelUserCollectionsWithOptions:nil];
// 获取所有资源的集合,并按资源的创建时间排序
PHFetchOptions *options = [[PHFetchOptions alloc] init];
options.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:YES]];
PHFetchResult *assetsFetchResults = [PHAsset fetchAssetsWithOptions:options];
如前面提到过的那样,从PHAssetCollection获取中获取到的可以是相册也可以是资源, 但无论是那种内容, 都统一使用PHFetchResult对象封装起来, 因此虽然PHAssetCollection回去到的结果可以是多样的, 但通过PHFetchResult就可以使用统一的方法去处理这些内容(即遍历PHFetchResult);
例子:
// 列出所有相册智能相册
PHFetchResult *smartAlbums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum subtype:PHAssetCollectionSubtypeAlbumRegular options:nil];
// 这时 smartAlbums 中保存的应该是各个智能相册对应的 PHAssetCollection
for (NSInteger i = 0; i < fetchResult.count; i++) {
// 获取一个相册(PHAssetCollection)
PHCollection *collection = fetchResult[i];
if ([collection isKindOfClass:[PHAssetCollection class]]) {
PHAssetCollection *assetCollection = (PHAssetCollection *)collection;
// 从每一个智能相册中获取到的 PHFetchResult 中包含的才是真正的资源(PHAsset)
PHFetchResult *fetchResult = [PHAsset fetchAssetsInAssetCollection:assetCollection options:fetchOptions];
else {
NSAssert(NO, @"Fetch collection not PHCollection: %@", collection);
}
}
// 获取所有资源的集合,并按资源的创建时间排序
PHFetchOptions *options = [[PHFetchOptions alloc] init];
options.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:YES]];
PHFetchResult *assetsFetchResults = [PHAsset fetchAssetsWithOptions:options];
// 这时 assetsFetchResults 中包含的,应该就是各个资源(PHAsset)
for (NSInteger i = 0; i < fetchResult.count; i++) {
// 获取一个资源(PHAsset)
PHAsset *asset = fetchResult[i];
}
2)、获取图像的方式与坑点:
PhotoKit无法直接从PHAsset的实体中获取图像,而引入了一个管理器PHImageManager获取图像,PHImageManager是通过请求的方式拉取图像,并可以控制请求得到的图像的尺寸、剪切方式、质量、缓存及请求本身的管理(发出请求、取消请求)等,而请求图像的方法是PHImageManager的一个实例方法:
- (PHImageRequestID)requestImageForAsset:(PHAsset *)asset
targetSize:(CGSize)targetSize
contentMode:(PHImageContentMode)contentMode
options:(nullable PHImageRequestOptions *)options
resultHandler:(void (^)(UIImage *__nullable result, NSDictionary *__nullable info))resultHandler;
这个方法中的参数坑点不少,下面逐个参数举例一下其作用及坑点:
Asset: 图像对应的PHAsset。
targetSize: 需要获取的图像的尺寸, 如果输入的尺寸大于资源原图的尺寸, 则需要返回原图。
注意:在PHImageManager中, 所有的尺寸都是用Pixel作为单位(note that all sizes are in pixels),因此这里想要获取正确大大小图像, 需要把输入的尺寸转为Pixel。如果需要返回原图尺寸,可以传入PhotoKit中预先定义到的常量PHImageManagerMaximumSize, 表示返回可选范围的最大尺寸, 即原图尺寸;
contentMode: 图像的剪切方式, 与UIView的contentMode参数相似,控制照片应该按比例缩放还是按比例填充的方式放到最终展示容器内;
注意:如果targetSize传入PHImageManagerMaximumSize, 则contentMode无论传入什么指都会被视为PHImageContentModeDefault。
options: 一个PHImageRequestOptions的实例,可以控制的内容相当丰富, 包含图形的质量、版本,也会有参数控制图像的剪切,下面在展开说明;
resultHandler:请求结束后辈调用的Block, 返回哟呵包含资源对于图像的UIImage和包含图像信息的一个NSDictionary, 在整个请求的周期中, 这个block可能会被调用多次,关于这点连同options参数在下面展开说明;
3)、PHImageRequestOptions与iCloud照片库:
PHImageRequestOptions中包含了一系列控制请求图像的属性。
①、 resizeMode属性控制图像的剪切,不知道为什么PhotoKit会在请求图像方法(requestImageForAsset)中已经有控制图像剪切的参数后(contentMode),还在options中加入控制剪切属性, 但如果两个地方所控制的剪切结果有所冲突, PhotoKit会以resizeMode的结果为准。另外,resizeMode也有控制图形质量的作用,如resizeMode设置为PHImageRequestOptionsResizeModeExact则返回图像必须和目标代销匹配,并且图形质量也为高质量图像,而设置为PHImageRequestOptionsResizeModeFast则请求的效率更高,但返回的图像可能和目标大小不一样并且质量较低。
②、在PhotoKit中, 对iCloud照片库有很好的支持,如果用户开启了iCloud照片库, 并且选择了“优化iPhone、iPad存储空间”, 或者选择了“下载并保留原件”但原件内有加载好的时候, PhotoKit也会预选拿到这些非本地图像的PHAsset, 但是由于本地并没有原图,所以如果产生了请求高清图的请求, PhotoKit会尝试从iCloud现在图片, 而这个行为最终的表现,会被PHImageRequestOptions中的值所影响。PHImageRequestOptions中常常会用到几个属性:
例如:
1、networkAccessAllowed:此参数控制时候允许网络请求, 默认是NO。如果不允许网络请求, 那么不用设置;当然也拉取不到iCloud的图像原件;
2、deliveryMode: 则用于控制请求图片质量;
3、synchronous: 控制是否为同步请求, 默认为NO,如果为YES, 即同步请求时,deliveryMode会被视为PHImageRequestOptionsDeliveryModeHighQualityFormat, 即自动返回高质量的图片, 因此不建议使用同步请求,否则如果界面需要等待返回的图像才能进一步做出反应,则反应时长会很长。
4、progressHandler: 当图片需要从iCloud下载时, 这个block会被自动调用,block中会返回图像下载的进度、图像的信息、出错信息。开发者可以利用这些信息反馈给用户当前图形的现在进度以及状态。但需要注意progressHandler不在主线程上执行, 因此在其中需要操作UI, 则需要手动放到主线程执行;
5、versions: 这个属性是指获取的图像是否包含系统相册“编辑”功能处理过的信息(如滤镜、旋转等), 这点比ALAssetLibrary要灵活,
③、上面提到,requestImageForAsset中的参数resultHandler可能会被多次调用,这种情况就是图片需要从iCloud中下载的情况,在requestImageForAsset中返回的内容中,一开始的那一次请求中会返回一个小尺寸的图像版本,当高清图像还在下载时, 开发者可以首先给用户展示这个低清的图像版本,然后block在多次调用后, 最终返回高清的原图, 至于当前返回的图像是哪儿个版本图像,可以通过block返回的NSDictionary info中获知,PHImageResultlsDegradedKey表示当前返回的UIImage是低清图,如果判断是否已经获得高清图,可以这样判断:
// 排除取消,错误,低清图三种情况,即已经获取到了高清图
BOOL downloadFinined = ![[info objectForKey:PHImageCancelledKey] boolValue] && ![info objectForKey:PHImageErrorKey] && ![[info objectForKey:PHImageResultIsDegradedKey] boolValue];
注意:
当我们使用requestImageForAsset发出对图像的请求时, 如果在同一个PHImageManager中同时对同一个资源发出图像请求, 请求的进度是可以共享的,因此我们可以利用这个特性, 吧PHImageManager以单例的形式使用, 这样在切换界面时也不用担心无法传递原图的下载进度, 例如:在图像的列表页面触发了下载图像, 当我们离开列表页面进入预览大图界面时, 并不用担心会重新下载图像, 只要没有手动取消图像下载, 进入大图预览界面下载图像会自动继续从上次的仅需下载图像。
如果需要取消下载图像,则可以使用PHImageManager的cancelImageRequest方法, 它传入的是请求图像的请求ID, 这个ID可以从requestImageForAsset的返回值中获得,当然也可以从前面提到的包含图形信息的NSDictionary info中获得,当然前提是这和接受取消的PHImageManager与刚刚发送请求的PHImageManager是同一个实例,如上面所述式样单例是最为简单有效的方式;
4)、获取图像的优化:
PHImageManager提供了一个子类PHImageCachingManager用于处理图像的缓存,但是这个子类并不只是图像本身的缓存, 而是更加实用—处理图形的整个加载过程的缓存
- (void)startCachingImagesForAssets:(NSArray *)assets targetSize:(CGSize)targetSize contentMode:(PHImageContentMode)contentMode options:(nullable PHImageRequestOptions *)options;
总结更为简易可行的缓存方法:
获取图像时尽量获取预览图,不要直接显示原件,建议获取与设备屏统样大小的图像即可,实际系统相册预览大图时使用的也是预览图,这也是系统相册加载速度快的原因。
获取图片使用异步请求, 如上面所述, 当请求为异步时返回图像的block会被多次调用,先返回低清图,再返回高清图, 这样一来可以大大减少UI的等待时间。
获取到高清图后可以缓存下来, 简单的使用变量缓存即可,尽量在获取高清图后避免再次发起请求获取图像, 因为即使图像原件已经下载了,从新请求高清图时因为图片的尺寸比较大, 因此系统会生成图形和剪切图形也会话费一些时间;
预先加载图像, 如像预览大图这类情景中, 用户同时只会看到一张大图,因此在观看某一张图片时,预先请求其邻近两张图片, 对于加快UI响应很有帮助。
三、常用方法的封装:
1)、原图:
由于原图的尺寸通常会比较大,因此建议使用异步拉取, 但这里仍同时列举同步拉取方法,这里需要留意如前文总所述,ALAssetRepresentation中获取原图的接口fullResolutionImage所得到的图像并没有带上系统相册“编辑”(选择, 滤镜等)效果。 需要额外获取这些效果并手工叠加到图像上。
.h文件
/// Asset 的原图(包含系统相册“编辑”功能处理后的效果)
- (UIImage *)originImage;
/**
* 异步请求 Asset 的原图,包含了系统照片“编辑”功能处理后的效果(剪裁,旋转和滤镜等),可能会有网络请求
*
* @param completion 完成请求后调用的 block,参数中包含了请求的原图以及图片信息,在 iOS 8.0 或以上版本中,
* 这个 block 会被多次调用,其中第一次调用获取到的尺寸很小的低清图,然后不断调用,直接获取到高清图,
* 获取到高清图后 QMUIAsset 会缓存起这张高清图,这时 block 中的第二个参数(图片信息)返回的为 nil。
* @param phProgressHandler 处理请求进度的 handler,不在主线程上执行,在 block 中修改 UI 时注意需要手工放到主线程处理。
*
* @wraning iOS 8.0 以下中并没有异步请求预览图的接口,因此实际上为同步请求,这时 block 中的第二个参数(图片信息)返回的为 nil。
*
* @return 返回请求图片的请求 id
*/
- (NSInteger)requestOriginImageWithCompletion:(void (^)(UIImage *, NSDictionary *))completion withProgressHandler:(PHAssetImageProgressHandler)phProgressHandler;
.m文件
- (UIImage *)originImage {
if (_originImage) {
return _originImage;
}
__block UIImage *resultImage;
if (_usePhotoKit) {
PHImageRequestOptions *phImageRequestOptions = [[PHImageRequestOptions alloc] init];
phImageRequestOptions.synchronous = YES;
[[[QMUIAssetsManager sharedInstance] phCachingImageManager] requestImageForAsset:_phAsset
targetSize:PHImageManagerMaximumSize
contentMode:PHImageContentModeDefault
options:phImageRequestOptions
resultHandler:^(UIImage *result, NSDictionary *info) {
resultImage = result;
}];
} else {
CGImageRef fullResolutionImageRef = [_alAssetRepresentation fullResolutionImage];
// 通过 fullResolutionImage 获取到的的高清图实际上并不带上在照片应用中使用“编辑”处理的效果,需要额外在 AlAssetRepresentation 中获取这些信息
NSString *adjustment = [[_alAssetRepresentation metadata] objectForKey:@"AdjustmentXMP"];
if (adjustment) {
// 如果有在照片应用中使用“编辑”效果,则需要获取这些编辑后的滤镜,手工叠加到原图中
NSData *xmpData = [adjustment dataUsingEncoding:NSUTF8StringEncoding];
CIImage *tempImage = [CIImage imageWithCGImage:fullResolutionImageRef];
NSError *error;
NSArray *filterArray = [CIFilter filterArrayFromSerializedXMP:xmpData
inputImageExtent:tempImage.extent
error:&error];
CIContext *context = [CIContext contextWithOptions:nil];
if (filterArray && !error) {
for (CIFilter *filter in filterArray) {
[filter setValue:tempImage forKey:kCIInputImageKey];
tempImage = [filter outputImage];
}
fullResolutionImageRef = [context createCGImage:tempImage fromRect:[tempImage extent]];
}
}
// 生成最终返回的 UIImage,同时把图片的 orientation 也补充上去
resultImage = [UIImage imageWithCGImage:fullResolutionImageRef scale:[_alAssetRepresentation scale] orientation:(UIImageOrientation)[_alAssetRepresentation orientation]];
}
_originImage = resultImage;
return resultImage;
}
- (NSInteger)requestOriginImageWithCompletion:(void (^)(UIImage *, NSDictionary *))completion withProgressHandler:(PHAssetImageProgressHandler)phProgressHandler {
if (_usePhotoKit) {
if (_originImage) {
// 如果已经有缓存的图片则直接拿缓存的图片
if (completion) {
completion(_originImage, nil);
}
return 0;
} else {
PHImageRequestOptions *imageRequestOptions = [[PHImageRequestOptions alloc] init];
imageRequestOptions.networkAccessAllowed = YES; // 允许访问网络
imageRequestOptions.progressHandler = phProgressHandler;
return [[[QMUIAssetsManager sharedInstance] phCachingImageManager] requestImageForAsset:_phAsset targetSize:PHImageManagerMaximumSize contentMode:PHImageContentModeDefault options:imageRequestOptions resultHandler:^(UIImage *result, NSDictionary *info) {
// 排除取消,错误,低清图三种情况,即已经获取到了高清图时,把这张高清图缓存到 _originImage 中
BOOL downloadFinined = ![[info objectForKey:PHImageCancelledKey] boolValue] && ![info objectForKey:PHImageErrorKey] && ![[info objectForKey:PHImageResultIsDegradedKey] boolValue];
if (downloadFinined) {
_originImage = result;
}
if (completion) {
completion(result, info);
}
}];
}
} else {
if (completion) {
completion([self originImage], nil);
}
return 0;
}
}
2)、缩略图
相对于在拉取原图时 ALAssetLibrary 的部分需要手工叠加系统相册的“编辑”效果,拉取缩略图则简单一些,因为系统接口拉取到的缩略图已经带上“编辑”的效果了。
.h文件
/**
* Asset 的缩略图
*
* @param size 指定返回的缩略图的大小,仅在 iOS 8.0 及以上的版本有效,其他版本则调用 ALAsset 的接口由系统返回一个合适当前平台的图片
*
* @return Asset 的缩略图
*/
- (UIImage *)thumbnailWithSize:(CGSize)size;
/**
* 异步请求 Asset 的缩略图,不会产生网络请求
*
* @param size 指定返回的缩略图的大小,仅在 iOS 8.0 及以上的版本有效,其他版本则调用 ALAsset 的接口由系统返回一个合适当前平台的图片
* @param completion 完成请求后调用的 block,参数中包含了请求的缩略图以及图片信息,在 iOS 8.0 或以上版本中,这个 block 会被多次调用,
* 其中第一次调用获取到的尺寸很小的低清图,然后不断调用,直接获取到高清图,获取到高清图后 QMUIAsset 会缓存起这张高清图,
* 这时 block 中的第二个参数(图片信息)返回的为 nil。
*
* @return 返回请求图片的请求 id
*/
- (NSInteger)requestThumbnailImageWithSize:(CGSize)size completion:(void (^)(UIImage *, NSDictionary *))completion;
.m 文件
- (UIImage *)thumbnailWithSize:(CGSize)size {
if (_thumbnailImage) {
return _thumbnailImage;
}
__block UIImage *resultImage;
if (_usePhotoKit) {
PHImageRequestOptions *phImageRequestOptions = [[PHImageRequestOptions alloc] init];
phImageRequestOptions.resizeMode = PHImageRequestOptionsResizeModeExact;
// 在 PHImageManager 中,targetSize 等 size 都是使用 px 作为单位,因此需要对targetSize 中对传入的 Size 进行处理,宽高各自乘以 ScreenScale,从而得到正确的图片
[[[QMUIAssetsManager sharedInstance] phCachingImageManager] requestImageForAsset:_phAsset
targetSize:CGSizeMake(size.width * ScreenScale, size.height * ScreenScale)
contentMode:PHImageContentModeAspectFill options:phImageRequestOptions
resultHandler:^(UIImage *result, NSDictionary *info) {
resultImage = result;
}];
} else {
CGImageRef thumbnailImageRef = [_alAsset thumbnail];
if (thumbnailImageRef) {
resultImage = [UIImage imageWithCGImage:thumbnailImageRef];
}
}
_thumbnailImage = resultImage;
return resultImage;
}
- (NSInteger)requestThumbnailImageWithSize:(CGSize)size completion:(void (^)(UIImage *, NSDictionary *))completion {
if (_usePhotoKit) {
if (_thumbnailImage) {
if (completion) {
completion(_thumbnailImage, nil);
}
return 0;
} else {
PHImageRequestOptions *imageRequestOptions = [[PHImageRequestOptions alloc] init];
imageRequestOptions.resizeMode = PHImageRequestOptionsResizeModeExact;
// 在 PHImageManager 中,targetSize 等 size 都是使用 px 作为单位,因此需要对targetSize 中对传入的 Size 进行处理,宽高各自乘以 ScreenScale,从而得到正确的图片
return [[[QMUIAssetsManager sharedInstance] phCachingImageManager] requestImageForAsset:_phAsset targetSize:CGSizeMake(size.width * ScreenScale, size.height * ScreenScale) contentMode:PHImageContentModeAspectFill options:imageRequestOptions resultHandler:^(UIImage *result, NSDictionary *info) {
// 排除取消,错误,低清图三种情况,即已经获取到了高清图时,把这张高清图缓存到 _thumbnailImage 中
BOOL downloadFinined = ![[info objectForKey:PHImageCancelledKey] boolValue] && ![info objectForKey:PHImageErrorKey] && ![[info objectForKey:PHImageResultIsDegradedKey] boolValue];
if (downloadFinined) {
_thumbnailImage = result;
}
if (completion) {
completion(result, info);
}
}];
}
} else {
if (completion) {
completion([self thumbnailWithSize:size], nil);
}
return 0;
}
}
3)、 预览图
与上面的方法类似,不再展开说明。
.h 文件
/**
* Asset 的预览图
*
* @warning 仿照 ALAssetsLibrary 的做法输出与当前设备屏幕大小相同尺寸的图片,如果图片原图小于当前设备屏幕的尺寸,则只输出原图大小的图片
* @return Asset 的全屏图
*/
- (UIImage *)previewImage;
/**
* 异步请求 Asset 的预览图,可能会有网络请求
*
* @param completion 完成请求后调用的 block,参数中包含了请求的预览图以及图片信息,在 iOS 8.0 或以上版本中,
* 这个 block 会被多次调用,其中第一次调用获取到的尺寸很小的低清图,然后不断调用,直接获取到高清图,
* 获取到高清图后 QMUIAsset 会缓存起这张高清图,这时 block 中的第二个参数(图片信息)返回的为 nil。
* @param phProgressHandler 处理请求进度的 handler,不在主线程上执行,在 block 中修改 UI 时注意需要手工放到主线程处理。
*
* @wraning iOS 8.0 以下中并没有异步请求预览图的接口,因此实际上为同步请求,这时 block 中的第二个参数(图片信息)返回的为 nil。
*
* @return 返回请求图片的请求 id
*/
- (NSInteger)requestPreviewImageWithCompletion:(void (^)(UIImage *, NSDictionary *))completion withProgressHandler:(PHAssetImageProgressHandler)phProgressHandler;
.m 文件
- (UIImage *)previewImage {
if (_previewImage) {
return _previewImage;
}
__block UIImage *resultImage;
if (_usePhotoKit) {
PHImageRequestOptions *imageRequestOptions = [[PHImageRequestOptions alloc] init];
imageRequestOptions.synchronous = YES;
[[[QMUIAssetsManager sharedInstance] phCachingImageManager] requestImageForAsset:_phAsset
targetSize:CGSizeMake(SCREEN_WIDTH, SCREEN_HEIGHT)
contentMode:PHImageContentModeAspectFill
options:imageRequestOptions
resultHandler:^(UIImage *result, NSDictionary *info) {
resultImage = result;
}];
} else {
CGImageRef fullScreenImageRef = [_alAssetRepresentation fullScreenImage];
resultImage = [UIImage imageWithCGImage:fullScreenImageRef];
}
_previewImage = resultImage;
return resultImage;
}
- (NSInteger)requestPreviewImageWithCompletion:(void (^)(UIImage *, NSDictionary *))completion withProgressHandler:(PHAssetImageProgressHandler)phProgressHandler {
if (_usePhotoKit) {
if (_previewImage) {
// 如果已经有缓存的图片则直接拿缓存的图片
if (completion) {
completion(_previewImage, nil);
}
return 0;
} else {
PHImageRequestOptions *imageRequestOptions = [[PHImageRequestOptions alloc] init];
imageRequestOptions.networkAccessAllowed = YES; // 允许访问网络
imageRequestOptions.progressHandler = phProgressHandler;
return [[[QMUIAssetsManager sharedInstance] phCachingImageManager] requestImageForAsset:_phAsset targetSize:CGSizeMake(SCREEN_WIDTH, SCREEN_HEIGHT) contentMode:PHImageContentModeAspectFill options:imageRequestOptions resultHandler:^(UIImage *result, NSDictionary *info) {
// 排除取消,错误,低清图三种情况,即已经获取到了高清图时,把这张高清图缓存到 _previewImage 中
BOOL downloadFinined = ![[info objectForKey:PHImageCancelledKey] boolValue] && ![info objectForKey:PHImageErrorKey] && ![[info objectForKey:PHImageResultIsDegradedKey] boolValue];
if (downloadFinined) {
_previewImage = result;
}
if (completion) {
completion(result, info);
}
}];
}
} else {
if (completion) {
completion([self previewImage], nil);
}
return 0;
}
}
4)、方向(imageOrientation)
比较奇怪的是,无论在 PhotoKit 或者是 ALAssetLibrary 中,要想获取到准确的图像方向,只能通过某些 key 检索所得。
.h 文件
- (UIImageOrientation)imageOrientation;
.m文件
- (UIImageOrientation)imageOrientation {
UIImageOrientation orientation;
if (_usePhotoKit) {
if (!_phAssetInfo) {
// PHAsset 的 UIImageOrientation 需要调用过 requestImageDataForAsset 才能获取
[self requestPhAssetInfo];
}
// 从 PhAssetInfo 中获取 UIImageOrientation 对应的字段
orientation = (UIImageOrientation)[_phAssetInfo[@"orientation"] integerValue];
} else {
orientation = (UIImageOrientation)[[_alAsset valueForProperty:@"ALAssetPropertyOrientation"] integerValue];
}
return orientation;
}