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

这里接着前文《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;

}

时间: 2024-10-02 00:15:47

iOS 开发之照片框架详解之二 —— PhotoKit 详解(下)的相关文章

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

一. 概况 本文接着 iOS 开发之照片框架详解,侧重介绍在前文中简单介绍过的 PhotoKit 及其与 ALAssetLibrary 的差异,以及如何基于 PhotoKit 与 AlAssetLibrary 封装出通用的方法. 这里引用一下前文中对 PhotoKit 基本构成的介绍: PHAsset: 代表照片库中的一个资源,跟 ALAsset 类似,通过 PHAsset 可以获取和保存资源 PHFetchOptions: 获取资源时的参数,可以传 nil,即使用系统默认值 PHAssetCo

iOS 开发之照片框架详解

一. 概要 在 iOS 设备中,照片和视频是相当重要的一部分.最近刚好在制作一个自定义的 iOS 图片选择器,顺便整理一下 iOS 中对照片框架的使用方法.在 iOS 8 出现之前,开发者只能使用 AssetsLibrary 框架来访问设备的照片库,这是一个有点跟不上 iOS 应用发展步伐以及代码设计原则但确实强大的框架,考虑到 iOS7 仍占有不少的渗透率,因此 AssetsLibrary 也是本文重点介绍的部分.而在 iOS8 出现之后,苹果提供了一个名为 PhotoKit 的框架,一个可以

[转] iOS --- ReactiveCocoa - iOS开发的新框架

转载唐巧的博客:ReactiveCocoa - iOS开发的新框架

iOS开发——淫技篇&iOS开发中各种淫技总结(二)

iOS开发中各种淫技总结(二) 先来张笔者电脑上面安装的Mac app 一:for .. in 的内部实现(swift): 1 var g = array.generate() 2 while let obj = g.next() { 3 4 5 print(obj) } 6 二:map/Fileter/Reduce map map方法,其获取一个闭包表达式作为其唯一参数. 数组中的每一个元素调用一次该闭包函数,并返回该元素所映射的值(也可以是不同类型的值). 具体的映射方式和返回值类型由闭包来

上门洗车APP --- Androidclient开发 之 网络框架封装介绍(二)

上门洗车APP --- Androidclient开发 之 网络框架封装介绍(二) 前几篇博文中给大家介绍了一下APP中的基本业务及开发本项目使用的网络架构: 上门洗车APP --- Androidclient开发 前言及业务简单介绍 上门洗车APP --- Androidclient开发 之 网络框架封装介绍(一) 本篇接着给大家分享网络框架封装.相信感兴趣的朋友已经对上篇博文中的一些开源项目有了些许了解.这里继续为大家介绍关于GenericDataManager 通用网络管理类中的 data

ViewPager 详解(二)---详解四大函数

前言:上篇中我们讲解了如何快速实现了一个滑动页面,但问题在于,PageAdapter必须要重写的四个函数,它们都各有什么意义,在上节的函数内部为什么要这么实现,下面我们就结合Android的API说明,详细讲解一下. 相关文章: 1.<ViewPager 详解(一)---基本入门> 2.<ViewPager 详解(二)---详解四大函数> 3.<ViewPager 详解(三)---PagerTabStrip与PagerTitleStrip添加标题栏的异同> 4.<

iOS开发——数据持久化OC篇&amp;(五)SQLite3详解

SQLite3详解 SQLite是嵌入式的和轻量级的SQL数据库.SQLite是由C实现的.广泛用于包括浏览器(支持HTML5的大部分浏览器,IE除外).iOS.Android以及一些便携需求的小型web应用系统. 1 使用原因:存储.检索信息 2 SQLite是MySQL精简版.但无需服务器就能进行. 3 两个限制:1)必须手动创建数据库 2)没有面向对象的接口. 4 如何手动创建数据库. 使用SQLite前的准备 使用SQLite是很多做iOS开发中第一次面对C的情况,包括我.因为SQLit

iOS开发——数据持久化OC篇&amp;(六)CoreData详解

CoreData详解 介绍: 在Cocoa环境下,如果你想使用数据库(如sqlite),你可以使用sql语句的方式通过相关的工具类进行数据库的直接操作.当然你也可以通过别人封装之后的一些简单框架,使得你的操作更加简单(如FMDB BNRPersistence). Cocoa框架本身提供了CoreData这个API可方便的让开发者通过操作对象的方式在操作数据库.CoreData是一个对象图(object graph)以及持久化的管理框架.我们可以通过CoreData创对象,设置好象之间的关系,然后

ReactiveCocoa - iOS开发的新框架

本文转载至 http://www.infoq.com/cn/articles/reactivecocoa-ios-new-develop-framework ReactiveCocoa(其简称为RAC)是由Github 开源的一个应用于iOS和OS X开发的新框架.RAC具有函数式编程和响应式编程的特性.它主要吸取了.Net的 Reactive Extensions的设计和实现.本文将详细介绍该框架试图解决什么问题,以及其用法与特点. ReactiveCocoa试图解决什么问题 经过一段时间的研