相册取出照片是进行开发app时候经常使用到的一个功能
Apple官方封装好的工具里只允许一次取出单张照片。(Uiimagepicker)
不能进行多张照片的选择。
然而在我们的实际开发中。 一次选取多张照片已经是一个频繁出现的功能。
这个功能要靠我们开发者自己通过Api来实现。
在现在的ios系统中。
Apple给开发者提供了两套可以使用的框架。
AssetsLibrary 和 PhotoKit (ios8.0以后)
AssetsLibrary
AssetsLibrary 的逻辑结构比较容易理解,照片库中的完整照片库对象、相册、相片都可以找到相对应的类.
关于AssetsLibrary的简单介绍
AssetsLibrary: 代表整个设备中的资源库(照片库),通过 AssetsLibrary 可以获取和包括设备中的照片和视频
ALAssetsGroup: 映射照片库中的一个相册,通过 ALAssetsGroup 可以获取某个相册的信息,相册下的资源,同时也可以对某个相册添加资源。
ALAsset: 映射照片库中的一个照片或视频,通过 ALAsset 可以获取某个照片或视频的详细信息,或者保存照片和视频。
ALAssetRepresentation: ALAssetRepresentation 是对 ALAsset 的封装(但不是其子类),可以更方便地获取 ALAsset 中的资源信息,每个 ALAsset 都有至少有一个 ALAssetRepresentation 对象,可以通过 defaultRepresentation 获取。而例如使用系统相机应用拍摄的 RAW + JPEG 照片,则会有两个 ALAssetRepresentation,一个封装了照片的 RAW 信息,另一个则封装了照片的 JPEG 信息。
简单使用
AssetsLibrary的这个方法可以枚举出本地的相册
- (void)enumerateGroupsWithTypes:(ALAssetsGroupType)types usingBlock:(ALAssetsLibraryGroupsEnumerationResultsBlock)enumerationBlock failureBlock:(ALAssetsLibraryAccessFailureBlock)failureBlock
例如
[assetLibrary enumerateGroupsWithTypes:ALAssetsGroupAllusingBlock:^(ALAssetsGroup *group ,BOOL *stop){
这里的group就是对应的一个相册
// 获取相册的名字
NSString *groupname = [groupvalueForProperty:ALAssetsGroupPropertyName];
}failureBlock:^(NSError *error) {}];
进一步遍历获取group中的元素(照片)
[group enumerateAssetsUsingBlock:^(ALAsset *result,NSUInteger index , BOOL *stop){
此处的 AlAsset 一一对应相册里面的每一张照片
下面来具体介绍几个AlAsset的常用属性
result.thumbnail(小正方形的缩略图)
- result.defaultRepresentation.url (元素的路径)
- 其他的属性开发者可以自己进Api看 都比较直观
}];
注意:
AlAssetsLibrary在使用时候一定要用强引用
__strong ALAssetsLibrary *assetLibrary;
assetLibrary = [self defaultAssetsLibrary];
在使用AlAssetsLibrary时候最好使用单利模式。
重复alloc时候会出现权限问题
-(ALAssetsLibrary *)defaultAssetsLibrary{
staticdispatch_once_t pred =0;
staticALAssetsLibrary *library =nil;
dispatch_once(&pred, ^{
library = [[ALAssetsLibraryalloc]init];
});
return library;
}
AssetsLibrary 遵循写入优先原则
写入优先也就是說,在利用 AssetsLibrary 读取资源的过程中,有任何其它的进程(不一定是同一个 App)在保存资源时,就会收到 ALAssetsLibraryChangedNotification,让用户自行中断读取操作。最常见的就是读取 fullResolutionImage 时,用进程在写入,由于读取 fullResolutionImage 耗时较长,很容易就会 exception。
开启 Photo Stream 容易导致 exception
本质上,这跟上面的 AssetsLibrary 遵循写入优先原则是同一个问题。如果用户开启了共享照片流(Photo Stream),共享照片流会以 mstreamd 的方式“偷偷”执行,当有人把相片写入 Camera Roll 时,它就会自动保存到 Photo Stream Album 中,如果用户刚好在读取,那就跟上面说的一样产生 exception 了。由于共享照片流是用户决定是否要开启的,所以开发者无法改变,但是可以通过下面的接口在需要保护的时刻关闭监听共享照片流产生的频繁通知信息。
[ALAssetsLibrary disableSharedPhotoStreamsSupport];
======================================================================================================================================
PhotoKit
PhotoKit是苹果ios8.0以后提出的一个新的框架
这个框架相对于AssetsLibrary在对于iCloud中的照片的获取上有了很大的改善(如果你开启了iCloud并且选择了优化Iphone/ipad选项 那么你使用AssetsLibrary框架时候会取不出照片的原图,只能看到部分缩略图)
在取出方式上也有所改变。
AssetsLibrary在进行照片的取出本质是一个遍历的过程。
下面对PhotoKit中的类进行一系列简单的介绍:
PHAsset: 代表照片库中的一个资源,跟 ALAsset 类似,通过 PHAsset 可以获取和保存资源
PHFetchOptions: 获取资源时的参数,可以传 nil,即使用系统默认值
PHAssetCollection: PHCollection 的子类,表示一个相册或者一个时刻.
PHFetchResult: 表示一系列的资源结果集合,也可以是相册或者视频的集合,从PHCollection 的类方法中获得
PHImageManager: 用于处理资源的加载,加载图片的过程带有缓存处理,可以通过传入一个 PHImageRequestOptions 控制资源的输出尺寸等规格(注:如果不利用Options来控制取出的资源,那么我们取出的资源可能是无序的)
PHImageRequestOptions: 控制加载资源时的一系列参数
几个简单的方法
获取相册的集合,根据系统封装好的枚举
我们可以进行 系统相册 自定义相册 时刻相册 的获取
系统的枚举如下
typedef NS_ENUM(NSInteger, PHAssetCollectionType) {
PHAssetCollectionTypeAlbum = 1,
PHAssetCollectionTypeSmartAlbum = 2,
PHAssetCollectionTypeMoment = 3,
} NS_ENUM_AVAILABLE_IOS(8_0);
+ (PHFetchResult<PHAssetCollection *> *)fetchAssetCollectionsWithType:(PHAssetCollectionType)type subtype:(PHAssetCollectionSubtype)subtype options:(nullablePHFetchOptions *)options;
代码示例
------------------------------------------------------------------------------------------------------------------------------------------------------------
PHFetchResult<PHAssetCollection *> *assetCollectionsSystem = [PHAssetCollectionfetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbumsubtype:PHAssetCollectionSubtypeSmartAlbumUserLibraryoptions:nil];
NSMutableArray *collectionArray =[[NSMutableArrayalloc]init];
遍历自定义相册
for (PHAssetCollection *assetCollectionin assetCollectionsSystem) {
[collectionArray addObject:assetCollection];
}
PHFetchResult<PHAssetCollection *> *assetCollectionsIndex = [PHAssetCollectionfetchAssetCollectionsWithType:PHAssetCollectionTypeAlbumsubtype:PHAssetCollectionSubtypeAlbumRegularoptions:nil];
遍历系统相册
for (PHAssetCollection *assetCollectionin assetCollectionsIndex) {
if(![collectionArraycontainsObject:assetCollection]){
[collectionArray addObject:assetCollection];
}
}
------------------------------------------------------------------------------------------------------------------------------------------------------------
执行过上面的一段代码以后。
经过两次的遍历
第一次遍自定义相册
第二次便利系统相册
把这两次便利好的集合PHAssetCollection放到一个数组里。
这时候这个数组就包含了我们筛选过的自定义和系统的相册。
这也就比较好理解,为什么市面上这些app的混合相册列表,总是系统的和系统的相册在一起。自定义相册和自定义相册在一起
例如我安装了QQ和微博,我筛选的系统相册只要相机胶卷
那么我便利出的相册列表就是
------------------------------------------------
系统相册 相机胶卷
------------------------------------------------
自定义相册 QQ
微博
------------------------------------------------
-----------------------------------------------------------------------------------------------------------------------------------------------------------
进一步获取照片
在PhotoKit框架中
PHAsset获取照片时候不像 AlassetsLibrary遍历后直接使用ALAsset的属性就可以获取照片
而是出现了一个新的概念
PHImageManager
这个类是专门用来获取PHAsset对应的资源(可以是一个PHAsset 也可以是一个集合)
在这里只简单的列出一个获取单个PHAsset对应的资源,其它的方法也容易理解开发者可以自己看Api
- (PHImageRequestID)requestImageForAsset:(PHAsset *)asset targetSize:(CGSize)targetSize contentMode:(PHImageContentMode)contentMode options:(nullablePHImageRequestOptions *)options resultHandler:(void (^)(UIImage *__nullable result, NSDictionary *__nullable info))resultHandler;
简单应用
----------------------------------------------------
PHImageRequestOptions *options = [[PHImageRequestOptionsalloc] init];
options.networkAccessAllowed =YES;
// 同步获得图片, 只会返回1张图片
options.synchronous =YES;
NSLog(@"asset.localIdentifier --- %@",asset.localIdentifier);
// 照片尺寸
CGSize size = CGSizeMake(self.view.frame.size.width,self.view.frame.size.height) ;
// 从asset中获得图片
[[PHImageManagerdefaultManager] requestImageForAsset:assettargetSize:size contentMode:
PHImageContentModeDefaultoptions:options resultHandler:^(UIImage *_Nullable result, NSDictionary *
_Nullable info)
{
}];
----------------------------------------------------
这是版主写的一个简单的PhotokitDemo
https://github.com/fanfan19920219/PhotoLibraryTool.git
总结:
AlAssetslibrary这个框架主要是针对ios8一下的用户,它的优点就是简单易懂,逻辑清晰简单粗暴。缺点是遍历慢,耗费时间。
PhotoKit这个框架针对iCloud上的照片存取有很大的改进,并且在照片的大小获取上尤为灵活,获取照片的size可以根据开发者在不同的环境下使用不同的参数,可以更好的根据实际情况进行选择。并且在进行照片的取出时由于PHImageRequestOptions这个类的介入,在效率上有很大的提升。
缺点是在进行icloud的照片进行网络获取的时候,会锁死主线程带来不好的用户体验。
在使用这两个框架进行资源回调的时候都需要切换到主线程来进行操作(dispatch_get_mian......)
关于这两个框架版主还有很多没有探索到的地方。例如视频的使用获取以及存储,还有缓存机制的使用等等。
在后续的研究中会继续发表文章。
也欢迎各位大神指出我的不足,相互讨论。