首页 > 系统 > iOS > 正文

iOS 开发之照片框架详解之二 —— PhotoKit 详解(下)

2019-11-06 09:36:03
字体:
来源:转载
供稿:网友

转自:http://kayosite.com/ios-development-and-detail-of-photo-framework-part-three.html

这里接着前文《iOS 开发之照片框架详解之二 —— PhotoKit 详解(上)》,主要是干货环节,列举了如何基于 PhotoKit 与 AlAssetLibrary 封装出通用的方法。

三. 常用方法的封装

虽然 PhotoKit 的功能强大很多,但基于兼容 iOS 8.0 以下版本的考虑,暂时可能仍无法抛弃 ALAssetLibrary,这时候一个比较好的方案是基于 ALAssetLibrary 和 PhotoKit 封装出一系列模拟系统 Asset 类的自定义类,然后在其中封装好兼容 ALAssetLibrary 和 PhotoKit 的方法。

这里列举了四种常用的封装好的方法:原图,缩略图,预览图,方向,下面直接上代码,代码中有相关注释解释其中的要点。其中下面的代码中常常出现的 [[QMUIAssetsManager sharedInstance] phCachingImageManager] 是 QMUI 框架中封装的类以及单例方法,表示产生一个 PHCachingImageManager 的单例,这样做的好处是 PHCachingImageManager 需要占用较多的资源,因此使用单例可以避免无谓的资源消耗,另外请求图像等方法需要基于用一个 PHCachingImageManager 实例才能进行进度续传,管理请求等操作。

1. 原图

由于原图的尺寸通常会比较大,因此建议使用异步拉取,但这里仍同时列举同步拉取的方法。这里需要留意如前文中所述,ALAssetRePResentation 中获取原图的接口 fullResolutionImage 所得到的图像并没有带上系统相册“编辑”(选中,滤镜等)的效果,需要额外获取这些效果并手工叠加到图像上。

.h 文件

12345678910111213141516/// 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 文件

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374- (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;    returnresultImage;} - (NSInteger)requestOriginImageWithCompletion:(void(^)(UIImage *, NSDictionary*))completion withProgressHandler:(PHAssetImageProgressHandler)phProgressHandler {    if(_usePhotoKit) {        if(_originImage) {            // 如果已经有缓存的图片则直接拿缓存的图片            if(completion) {                completion(_originImage,nil);            }            return0;        }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 中                BOOLdownloadFinined = ![[info objectForKey:PHImageCancelledKey] boolValue] && ![info objectForKey:PHImageErrorKey] && ![[info objectForKey:PHImageResultIsDegradedKey] boolValue];                if(downloadFinined) {                    _originImage = result;                }                if(completion) {                    completion(result, info);                }            }];        }    }else{        if(completion) {            completion([selforiginImage], nil);        }        return0;    }}

 2. 缩略图

相对于在拉取原图时 ALAssetLibrary 的部分需要手工叠加系统相册的“编辑”效果,拉取缩略图则简单一些,因为系统接口拉取到的缩略图已经带上“编辑”的效果了。

.h 文件

1234567891011121314151617181920/** *  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 文件

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354- (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;    returnresultImage;} - (NSInteger)requestThumbnailImageWithSize:(CGSize)size completion:(void(^)(UIImage *, NSDictionary*))completion {    if(_usePhotoKit) {        if(_thumbnailImage) {            if(completion) {                completion(_thumbnailImage,nil);            }            return0;        }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 中                  BOOLdownloadFinined = ![[info objectForKey:PHImageCancelledKey] boolValue] && ![info objectForKey:PHImageErrorKey] && ![[info objectForKey:PHImageResultIsDegradedKey] boolValue];                  if(downloadFinined) {                      _thumbnailImage = result;                  }                  if(completion) {                      completion(result, info);                  }            }];        }    }else{        if(completion) {            completion([selfthumbnailWithSize:size], nil);        }        return0;    }}

 3. 预览图

与上面的方法类似,不再展开说明。

.h 文件

123456789101112131415161718192021/** *  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 文件

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253- (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;    returnresultImage;} - (NSInteger)requestPreviewImageWithCompletion:(void(^)(UIImage *, NSDictionary*))completion withProgressHandler:(PHAssetImageProgressHandler)phProgressHandler {    if(_usePhotoKit) {        if(_previewImage) {            // 如果已经有缓存的图片则直接拿缓存的图片            if(completion) {                completion(_previewImage,nil);            }            return0;        }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 中                BOOLdownloadFinined = ![[info objectForKey:PHImageCancelledKey] boolValue] && ![info objectForKey:PHImageErrorKey] && ![[info objectForKey:PHImageResultIsDegradedKey] boolValue];                if(downloadFinined) {                    _previewImage = result;                }                if(completion) {                    completion(result, info);                }            }];        }    }else{        if(completion) {            completion([selfpreviewImage], nil);        }        return0;    }}

 4. 方向(imageOrientation)

比较奇怪的是,无论在 PhotoKit 或者是 ALAssetLibrary 中,要想获取到准确的图像方向,只能通过某些 key 检索所得。

.h 文件

1- (UIImageOrientation)imageOrientation;

.m 文件

1234567891011121314- (UIImageOrientation)imageOrientation {    UIImageOrientation orientation;    if(_usePhotoKit) {        if(!_phAssetInfo) {            // PHAsset 的 UIImageOrientation 需要调用过 requestImageDataForAsset 才能获取            [selfrequestPhAssetInfo];        }        // 从 PhAssetInfo 中获取 UIImageOrientation 对应的字段        orientation = (UIImageOrientation)[_phAssetInfo[@"orientation"] integerValue];    }else{        orientation = (UIImageOrientation)[[_alAsset valueForProperty:@"ALAssetPropertyOrientation"] integerValue];    }    returnorientation;}

系列文章:iOS 开发之照片框架详解iOS 开发之照片框架详解之二 —— PhotoKit 详解(上)iOS 开发之照片框架详解之二 —— PhotoKit 详解(下)


上一篇:iOS-runloop了解

下一篇:iOS 开发命令汇总

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表