iOS 图片选择打造专属于自己的 ImagePicker

前文

从iOS8以后,Apple 就不再使用 AssetsLibrary 作为获取系统相册图片的方法了,转而在iOS8中推出了Photokit作为访问系统相册的库。官方对Photokit的概念解释为:

在iOS和macOS中,PhotoKit提供了支持为Photos应用构建照片编辑扩展的类。 在iOS和tvOS中,PhotoKit还可以直接访问由照片应用管理的照片和视频。
使用PhotoKit,您可以获取和缓存assets以进行显示和回放,编辑图像和视频内容,或管理assets集合,例如专辑,时刻和共享相册。

iOS 图片选择打造专属于自己的 ImagePicker_第1张图片
image

详细请见苹果开发者文档

开发

我们目前对Photokit还比较陌生,唯一能够让我们揭开它神秘面纱的方式就是自己动手去模仿一下系统相册,如何去获取系统内的所有照片资源,如何去获取所有的相册,以及如何将获取到的数据直观的展现给用户看将是本章内容我要展示给大家的。

第一步:环境配置

  • 在Xcode项目中加入头文件
#import 
  • 在Xcode中修改info.plist

    在info.plist中找到 Privacy - Photo Library Usage Description 选项,添加隐私请求权限说明,例如:淘宝想要访问您的相册。

第二步:认识 PhotoKit 对象

可能刚开始的时候,大家也都跟我一样常常分不清楚 PHAsset,PHFetchOptions,PHAssetCollection,PHFetchResult,PHImageManager,PHImageRequestOptions 这些的区别,在这里就跟大家一一简单的做一个说明,为下面更进一步开发做好铺垫。

PHAsset:照片库中图像,视频或 live 照片。

PHFetchOptions:一组选项控制选项包括过滤,排序和管理,用于影响在获取PHAsset或collection对象时照片返回的结果。

PHCollection:Photos asset collections 和 collection lists 的抽象超类。

PHAssetCollection:PHCollection 的子类,表示一个相册或者一个时刻,例如片刻,用户创建的相册或智能相册。

PHFetchResult:表示一系列的资源结果集合,也可以是相册的集合,从 PHCollection 的类方法中获得;

PHImageManager:提供用于检索或生成与PHAsset相关联的图像或视频数据的方法。

PHCachingImageManager:PHImageManager的子类,为了处理大量的PHAsset数据时提升性能,如果要使用照片或视频资源的缩略图填充UICollectionViewController 类似的UI,请使用PHCachingImageManager。

PHImageRequestOptions:控制图片加载时的一些参数,例如同步加载or异步加载,图片尺寸等。

PHVideoRequestOptions:控制视频加载时的一些参数,例如同步加载or异步加载,图片尺寸等。

PHLivePhotoRequestOptions:控制live图片加载时的一些参数,例如同步加载or异步加载,图片尺寸等。

第三步:PhotoKit 机制

PhotoKit是通过"Fetch"的方式去获取系统的相册资源,这些获取的方式都是通过一系列的API去调用完成的,具体使用哪个类方法,则需要了解获取的是相册、时刻还是资源,这类方法中的 option 充当了过滤器的作用,可以过滤相册的类型,日期,名称等,从而直接获取对应的资源。

获取相册

获取系统智能相册

    PHFetchResult *fetchResult = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum subtype:PHAssetCollectionSubtypeAny options:nil];

获取用户创建的相册

    PHFetchResult *topLevelUserCollections = [PHCollectionList fetchTopLevelUserCollectionsWithOptions:nil];

遍历相册列表

for(int i = 0; i < fetchResult.count; i++){
    PHCollection *collection = fetchResult[i];
    if([collection isKindOfClass:[PHAssetCollection class]]){
        PHAssetCollection *assetCollection = (PHAssetCollection *)collection;
        //相册名称
        NSString *title = assetCollection.localizedTitle;
    }
}

获取系统智能相册API中的subtype是由多个不同选择组成的枚举,根据你自己想要获取的数据需求来决定:

typedef NS_ENUM(NSInteger, PHAssetCollectionSubtype) {
    
    // PHAssetCollectionTypeAlbum regular subtypes
    PHAssetCollectionSubtypeAlbumRegular         = 2,
    PHAssetCollectionSubtypeAlbumSyncedEvent     = 3,
    PHAssetCollectionSubtypeAlbumSyncedFaces     = 4,
    PHAssetCollectionSubtypeAlbumSyncedAlbum     = 5,
    PHAssetCollectionSubtypeAlbumImported        = 6,
    
    // PHAssetCollectionTypeAlbum shared subtypes
    PHAssetCollectionSubtypeAlbumMyPhotoStream   = 100,
    PHAssetCollectionSubtypeAlbumCloudShared     = 101,
    
    // PHAssetCollectionTypeSmartAlbum subtypes
    PHAssetCollectionSubtypeSmartAlbumGeneric    = 200,
    PHAssetCollectionSubtypeSmartAlbumPanoramas  = 201,
    PHAssetCollectionSubtypeSmartAlbumVideos     = 202,
    PHAssetCollectionSubtypeSmartAlbumFavorites  = 203,
    PHAssetCollectionSubtypeSmartAlbumTimelapses = 204,
    PHAssetCollectionSubtypeSmartAlbumAllHidden  = 205,
    PHAssetCollectionSubtypeSmartAlbumRecentlyAdded = 206,
    PHAssetCollectionSubtypeSmartAlbumBursts     = 207,
    PHAssetCollectionSubtypeSmartAlbumSlomoVideos = 208,
    PHAssetCollectionSubtypeSmartAlbumUserLibrary = 209,
    PHAssetCollectionSubtypeSmartAlbumSelfPortraits PHOTOS_AVAILABLE_IOS_TVOS(9_0, 10_0) = 210,
    PHAssetCollectionSubtypeSmartAlbumScreenshots PHOTOS_AVAILABLE_IOS_TVOS(9_0, 10_0) = 211,
    PHAssetCollectionSubtypeSmartAlbumDepthEffect PHOTOS_AVAILABLE_IOS_TVOS(10_2, 10_1) = 212,
    PHAssetCollectionSubtypeSmartAlbumLivePhotos PHOTOS_AVAILABLE_IOS_TVOS(10_3, 10_2) = 213,
    PHAssetCollectionSubtypeSmartAlbumAnimated PHOTOS_AVAILABLE_IOS_TVOS(11_0, 11_0) = 214,
    PHAssetCollectionSubtypeSmartAlbumLongExposures PHOTOS_AVAILABLE_IOS_TVOS(11_0, 11_0) = 215,
    // Used for fetching, if you don't care about the exact subtype
    PHAssetCollectionSubtypeAny = NSIntegerMax
} PHOTOS_ENUM_AVAILABLE_IOS_TVOS(8_0, 10_0);

获取相册内照片

获取到我们的相册之后,我们接下来的工作就是要将相册内的照片,视频等数据显示在我们的网格视图中,但是如果直接用原图来做显示就显得极不恰当,Apple提供的PhotoKit框架为我们提供了解决方案。

获取相册内所有照片的缩略图
- (void)requestThumbnailImageWithSize:(PHAsset *)asset size:(CGSize)size
           completion:(void(^)(UIImage *result, NSDictionary *info))completion{
    PHCachingImageManager *phCachingImageManager = [[PHCachingImageManager alloc] init];
    PHImageRequestOptions *imageRequestOptions = [[PHImageRequestOptions alloc] init];
    imageRequestOptions.networkAccessAllowed = YES; // 允许访问网络
    imageRequestOptions.deliveryMode = PHImageRequestOptionsDeliveryModeOpportunistic;
    imageRequestOptions.synchronous = true;
    imageRequestOptions.resizeMode = PHImageRequestOptionsResizeModeFast;
    
    [phCachingImageManager requestImageForAsset:asset targetSize:size contentMode:PHImageContentModeAspectFill options:imageRequestOptions resultHandler:^(UIImage * _Nullable result, NSDictionary * _Nullable info) {
        if(completion){
            completion(result, info);
        }
    }];
}
获取预览图
- (void)requestPreviewImageWithCompletion:(PHAsset *)asset completion:(void(^)(UIImage *result, NSDictionary *info))completion{
    
    PHCachingImageManager *phCachingImageManager = [[PHCachingImageManager alloc] init];
    
    PHImageRequestOptions *imageRequestOptions = [[PHImageRequestOptions alloc] init];
    imageRequestOptions.networkAccessAllowed = YES; 
    imageRequestOptions.deliveryMode = PHImageRequestOptionsDeliveryModeOpportunistic;
    imageRequestOptions.synchronous = true;
    imageRequestOptions.resizeMode = PHImageRequestOptionsResizeModeFast;
    
    [phCachingImageManager requestImageForAsset:asset targetSize:CGSizeMake(kScreenWidth, kScreenHeight) contentMode:PHImageContentModeAspectFill options:imageRequestOptions resultHandler:^(UIImage * _Nullable result, NSDictionary * _Nullable info) {
        if(completion){
            completion(result, info);
        }
    }];
}
获取原图
- (void)requestOriginImageWithCompletion:(PHAsset *)asset completion:(void (^)(UIImage *result, NSDictionary *info))completion withProgressHandler:(PHAssetImageProgressHandler)phProgressHandler{
    
    PHCachingImageManager *phCachingImageManager = [[PHCachingImageManager alloc] init];
    
    PHImageRequestOptions *imageRequestOptions = [[PHImageRequestOptions alloc] init];
    imageRequestOptions.networkAccessAllowed = YES; // 允许访问网络
    imageRequestOptions.synchronous = true;
    imageRequestOptions.progressHandler = phProgressHandler;
    
    [phCachingImageManager requestImageForAsset:asset targetSize:PHImageManagerMaximumSize contentMode:PHImageContentModeAspectFill options:imageRequestOptions resultHandler:^(UIImage * _Nullable result, NSDictionary * _Nullable info) {
        if(completion){
            completion(result, info);
        }
    }];
}

上面的三个方法都调用了requestImageForAsset这个方法来请求图片,该方法的参数有多个,下面依次来讲解一下它们的作用:

  • asset:图片资源
  • targetSize:需要获取的图片尺寸,如果给定的尺寸与原图的尺寸比例不匹配,则下面要讲的参数contentMode将确定如何调整图像大小,如果需要返回原图尺寸,可以传入系统预先定义好的常量 PHImageManagerMaximumSize,表示返回原图尺寸
  • contentMode (PHImageContentMode):裁剪方式,可传入PHImageContentModeAspectFit,PHImageContentModeAspectFill和PHImageContentModeDefault,如果 targetSize 传入 PHImageManagerMaximumSize,则 contentMode 无论传入什么值都会被视为 PHImageContentModeDefault
  • options(PHImageRequestOptions):PHImageRequestOptions 中包含了一系列控制请求图像的属性分别如下:
  1. isNetworkAccessAllowed:参数控制是否允许网络请求;
  2. deliveryMode:用于控制请求的图片质量;PHImageRequestOptionsDeliveryModeOpportunistic: 如果是异步状态,图片会多次返回并且质量越来越高;PHImageRequestOptionsDeliveryModeHighQualityFormat:高清格式; PHImageRequestOptionsDeliveryModeFastFormat 加载反应速度最快的格式;
  3. synchronous:控制是否为同步请求,如果是同步则照片只返回一次;
  4. resizeMode:属性控制图像的剪裁;PHImageRequestOptionsResizeModeNone: 无裁剪;PHImageRequestOptionsResizeModeFast: 加载方式更快; PHImageRequestOptionsResizeModeExact: 返回与targetSize 保持一致的图片尺寸(如果normalizedCropRect被赋值则必须要设置);
  5. normalizedCropRect:裁剪区域设置;
  6. progressHandler:这是一个回调block,当图像需要从 iCloud 下载时,这个 block 会被自动调用,block 中会返回图像下载的进度、图像的信息、出错信息.如果需要更新UI则需要将progressHandler放到主线程上执行;
  • resultHandler((UIImage *__nullable result, NSDictionary *__nullable info)):求结束后被调用的 block,返回一个包含资源对于图像的 UIImage 和包含图像信息的一个 Dictionary;

当然,还有请求 livephoto 和请求video的方法,这里就不做篇幅去细说了,在接下来的文章中我们会讲到,感兴趣的可以先去了解一下它们的接口。

livephoto
- (PHImageRequestID)requestLivePhotoForAsset:(PHAsset *)asset targetSize:(CGSize)targetSize contentMode:(PHImageContentMode)contentMode options:(nullable PHLivePhotoRequestOptions *)options resultHandler:(void (^)(PHLivePhoto *__nullable livePhoto, NSDictionary *__nullable info))resultHandler PHOTOS_AVAILABLE_IOS_TVOS(9_1, 10_0);
video
- (PHImageRequestID)requestPlayerItemForVideo:(PHAsset *)asset options:(nullable PHVideoRequestOptions *)options resultHandler:(void (^)(AVPlayerItem *__nullable playerItem, NSDictionary *__nullable info))resultHandler;

小结

本篇文章就跟大家简单的介绍了一下PhotoKit几个常用对象的概念以及API的调用,在下篇文章中,我会继续给大家带来利用PhotoKit打造专属自己的Imagepicker的内容,最后跟大家总结一下开发中需要注意的地方:

  1. 拉取相册之前,要先请求用户权限;
[PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
        if(status == PHAuthorizationStatusAuthorized){
            NSLog(@"User has authorized this application to access photos data.");
        }else if(status == PHAuthorizationStatusDenied){
            NSLog(@"User has explicitly denied this application access to photos data.");
        }else if(status == PHAuthorizationStatusRestricted){
            NSLog(@"This application is not authorized to access photo data.");
        }else if(status == PHAuthorizationStatusNotDetermined){
            NSLog(@"User has not yet made a choice with regards to this application");
        }
    }];
  1. 请使用PHCachingImageManager对象来替换PHImageManager对象来拉取资源;由于需要经常使用PHImageCachingManager来获取图片,所以需要将PHImageCachingManager封装成一个单例来调用,避免开销过大;
  2. 如果PHImageRequestOptions设置为异步,requestImageForAsset 对象调用 requestImageForAsset函数后,回调的 block返回一个包含资源对于图像的 UIImage 和包含图像信息的一个 Dictionary,在整个请求的周期中,这个 block 可能会被多次调用;
  3. 获取图片时尽量获取预览图,不要直接显示原件,建议获取与设备屏幕同样大小的图像;

如果你觉得我写的不错,请关注我的微信公众号:HelloWorld沈杰,您的支持是我创作的动力:


iOS 图片选择打造专属于自己的 ImagePicker_第2张图片
image

你可能感兴趣的:(iOS 图片选择打造专属于自己的 ImagePicker)