iOS 开发之照片框架详解之二 —— PhotoKit 详解(下)
本文链接: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 文件
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
/// 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 文件
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
|
- (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 文件
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
/** * 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 文件
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
- (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 文件
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
/** * 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 文件
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
|
- (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 文件
|
1
|
- (UIImageOrientation)imageOrientation; |
.m 文件
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
- (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;} |
系列文章:
iOS 开发之照片框架详解
iOS 开发之照片框架详解之二 —— PhotoKit 详解(上)
iOS 开发之照片框架详解之二 —— PhotoKit 详解(下)
参考资料:
objc中国 - 照片框架
Example app using Photos framework
AssetsLibrary Framework Reference
iOS 开发之照片框架详解之二 —— PhotoKit 详解(下)的更多相关文章
- iOS 开发之照片框架详解(2)
一. 概况 本文接着 iOS 开发之照片框架详解,侧重介绍在前文中简单介绍过的 PhotoKit 及其与 ALAssetLibrary 的差异,以及如何基于 PhotoKit 与 AlAssetLib ...
- iOS 开发之照片框架详解
转载自:http://kayosite.com/ios-development-and-detail-of-photo-framework.html 一. 概要 在 iOS 设备中,照片和视频是相当重 ...
- iOS 开发之照片框架详解之二 —— PhotoKit 详解(上)
转载自:http://kayosite.com/ios-development-and-detail-of-photo-framework-part-two.html 一. 概况 本文接着 iOS 开 ...
- iOS 开发之照片框架详解(1)
http://kayosite.com/ios-development-and-detail-of-photo-framework.html/comment-page-1 一. 概要 在 iOS 设备 ...
- iOS 开发之照片框架详解(3)
http://kayosite.com/ios-development-and-detail-of-photo-framework-part-three.html 三. 常用方法的封装 虽然 Phot ...
- ViewPager 详解(二)---详解四大函数
前言:上篇中我们讲解了如何快速实现了一个滑动页面,但问题在于,PageAdapter必须要重写的四个函数,它们都各有什么意义,在上节的函数内部为什么要这么实现,下面我们就结合Android的API说明 ...
- 上门洗车APP --- Androidclient开发 之 网络框架封装介绍(二)
上门洗车APP --- Androidclient开发 之 网络框架封装介绍(二) 前几篇博文中给大家介绍了一下APP中的基本业务及开发本项目使用的网络架构: 上门洗车APP --- Androidc ...
- ReactiveCocoa - iOS开发的新框架
本文转载至 http://www.infoq.com/cn/articles/reactivecocoa-ios-new-develop-framework ReactiveCocoa(其简称为RAC ...
- iOS开发之使用XMPPFramework实现即时通信(二)
上篇的博客iOS开发之使用XMPPFramework实现即时通信(一)只是本篇的引子,本篇博客就给之前的微信加上即时通讯的功能,主要是对XMPPFramework的使用.本篇博客中用到了Spark做测 ...
随机推荐
- 线程的实现方法以及区别 extends Thread、implements Runable
/** 线程存在于进程当中,进程由系统创建. 创建新的执行线程有两种方法 注意: 线程复写run方法,然后用start()方法调用,其实就是调用的run()方法,只是如果直接启动run()方法, ...
- Hanoi汉诺塔问题——递归与函数自调用算法
题目描述 Description 有N个圆盘,依半径大小(半径都不同),自下而上套在A柱上,每次只允许移动最上面一个盘子到另外的柱子上去(除A柱外,还有B柱和C柱,开始时这两个柱子上无盘子),但绝不允 ...
- spring容器启动的加载过程(一)
使用spring,我们在web.xml都会配置ContextLoaderListener <listener> <listener-class> org.springframe ...
- android打包大小笔录
版本 4.6.1 1-1 游戏完整包的大小 1-2 1级代码压缩 1-3 2级代码压缩 1-4 3级代码压缩 2-1 删除核心战斗的部分资源 2-2 2级代码压缩 3-0 删除外部UI的资源 剩下 ...
- 移动前端不得不了解的HTML5 head 头标签(首篇)
HTML的头部内容特别多,有针对SEO的头部信息,也有针对移动设备的头部信息.而且各个浏览器内核以及各个国内浏览器厂商都有些自己的标签元素,有很多差异性.移动端的工作已经越来越成为前端工作的重要内容, ...
- ACM心路
又到了夏天了,这个季节最容易发春,最近上课效率怎么这么低,哎,,别发春了,好好学习,天天向上,现在想妹子,都是空想,没有什么实际意义,纯属浪费时间,浪费生命,不如节约点时间到学习上,不学无术,到嘴边的 ...
- python2.7学习记录之二
一.高级特性 1.切片取前3个元素用L[0:3]表示,从索引0开始取,直到索引3为止,但不包括索引3.如果第一个索引是0可省略.前10个数 每两个取一个L[:10:2],所有数 每5个取一个L[::5 ...
- C#连接ACCESS的一个问题
C# 连接ACCESS数据库有时候报 "Microsoft.Jet.Oledb.4.0"没有注册,其实,并不是真的没注册,可能是下面的原因 在菜单 “项目”的最下面 工程属性 菜单 ...
- 注解 @ 或者 Alt+/ 不提示 或者提示 no default propsals 解决方案
- Django:之安全、国际化和session
Django 安全 以下是关于Django安全的一些特征,它包括如何使基于Django的网站的一些建议. 关于安全的官方文档:https://docs.djangoproject.com/en/dev ...