????????有没有人和我一样,在最开始接触iOS开发的时候,碰到图片选择器用的是UIImagePickerController,后来发现产品和设计不会按照系统页面来设计,这个时候去github或者cocochina上找一个第三方库,修修改改,用到哪改到哪,管中窥豹,由于定制了计划,每月写两篇博客,借此机会,把这个框架梳理一下。
????????首先明确一点,这篇文章是了解一下 AssetsLibrary和Photos框架,并就实例进行说明解析,很简单的一个小demo,并不是现成拿过来直接就能用。(Photos虽然比AssetsLibrary用起来舒服太多,但是它是iOS8.0之后才出来的,注意一下适配)
????????市场上绝大部分图片选择器都大同小异,例如微信和微博:
其实就是两个列表,一个相册列表,一个图片列表,当然也可以直接理解为一个tableView,一个collectionView。
Photos
1.1、UITableView
-
授权、获取相册集合
/*
* 获取用户授权
*
* PHAuthorizationStatus 授权状态
*/
[PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
/*
* PHAuthorizationStatusNotDetermined 用户还没有进行授权操作
* PHAuthorizationStatusRestricted 用户关闭了访问相册的权限
* PHAuthorizationStatusDenied 拒绝用户访问这个应用程序有明确的图片数据
* PHAuthorizationStatusAuthorized 用户已授权
*/
if (status == PHAuthorizationStatusNotDetermined || status == PHAuthorizationStatusRestricted) {
/*
* 未通过用户授权,可弹出用户引导,UIAlertView(请在设备的\"设置-隐私-照片\"中允许访问照片)
*/
} else {
/*
* 用户已授权
*
* 获取相册集合,将其加入到tableView数据源中
*/
PHFetchResult *album = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum
subtype:PHAssetCollectionSubtypeAlbumRegular
options:nil];
[album enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
PHAssetCollection *collection = (PHAssetCollection *)obj;
// 这里需要注意下,我们数据源中存的是PHAssetCollection
[self.groups addObject:collection];
[self.tableView reloadData];
}];
}
}];
-
信息对照表
属性 | 描述 |
---|---|
PHAsset | 代表照片库中的一个资源,通过 PHAsset 可以获取和保存资源 |
PHFetchOptions | 获取资源时的参数,可以传 nil,即使用系统默认值 |
PHAssetCollection | PHCollection 的子类,表示一个相册或者一个时刻(例:最近删除,视频列表,收藏等) |
PHFetchResult | 表示一系列的资源结果集合(此处是相册集合),从PHCollection 的类方法中获得 |
PHImageManager | 用于处理资源的加载,加载图片的过程带有缓存处理,可以通过传入一个 PHImageRequestOptions 控制资源的输出尺寸等规格(上面cell刷新时代码) |
PHImageRequestOptions | 控制加载图片时的一系列参数 |
-
枚举
typedef NS_ENUM(NSInteger, PHAssetCollectionType) {
PHAssetCollectionTypeAlbum = 1, // 从iTunes同步的相册,以及用户在 Photos 中自己建立的相册
PHAssetCollectionTypeSmartAlbum = 2, // 相机相册
PHAssetCollectionTypeMoment = 3, // 自动生成的时间分组的相册
} PHOTOS_ENUM_AVAILABLE_IOS_TVOS(8_0, 10_0);
typedef NS_ENUM(NSInteger, PHAssetCollectionSubtype) {
// 用户在 Photos 中创建的相册,也就是我所谓的逻辑相册
PHAssetCollectionSubtypeAlbumRegular = 2,
// 使用 iTunes 从 Photos 照片库或者 iPhoto 照片库同步过来的事件
PHAssetCollectionSubtypeAlbumSyncedEvent = 3,
// 使用 iTunes 从 Photos 照片库或者 iPhoto 照片库同步的人物相册。
PHAssetCollectionSubtypeAlbumSyncedFaces = 4,
// 做了 AlbumSyncedEvent 应该做的事
PHAssetCollectionSubtypeAlbumSyncedAlbum = 5,
// 从相机或是外部存储导入的相册
PHAssetCollectionSubtypeAlbumImported = 6,
// 用户的 iCloud 照片流
PHAssetCollectionSubtypeAlbumMyPhotoStream = 100,
// 用户使用 iCloud 共享的相册
PHAssetCollectionSubtypeAlbumCloudShared = 101,
// 文档解释为非特殊类型的相册,主要包括从 iPhoto 同步过来的相册
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,
// 包含所有的Live Photo资源
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,
//包含所有类型
PHAssetCollectionSubtypeAny = NSIntegerMax
} PHOTOS_ENUM_AVAILABLE_IOS_TVOS(8_0, 10_0);
-
cell展示、赋值
- (void)refreshWithPHAssetCollection:(PHAssetCollection *)collection {
// 按时间生序
PHFetchOptions *option = [[PHFetchOptions alloc] init];
option.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:YES]];
/*
* 获取相册实体
*
* 根据实体拿到展示的三个属性:标题、图片个数、相册的第一张图片
*/
PHFetchResult *result = [PHAsset fetchAssetsInAssetCollection:collection options:option];
self.textLabel.text = collection.localizedTitle;
self.detailTextLabel.text = [NSString stringWithFormat:@"%ld",[result count]];
[result enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
PHAsset *asset = (PHAsset *)obj;
PHImageRequestOptions *imageOption = [[PHImageRequestOptions alloc] init];
imageOption.resizeMode = PHImageRequestOptionsResizeModeFast;
/*
* 坑一 networkAccessAllowed
* 是否允许网络访问,默认为NO
* 主要只是否可以从iCloud中下载图像,如果iPhone开启iCloud优化话存储空间,设置为NO是拿不到图片的,这里也只一个坑,需要注意一下
*/
imageOption.networkAccessAllowed = YES;
/*
* 坑二 synchronous
* 是否同步处理一个图像请求,默认是NO
* 这里一般设置为NO,requestImageForAsset 请求就会有两次回调。第一次返回一个低质量的图片(缩略图),用于占位显示;第二次返回的是一个高质量的图(原图)
* 如果设置为YES,请求就只有一次的回调,返回一个高质量的图(原图),会有卡顿现象(线程阻塞)
*/
imageOption.synchronous = NO;
[[PHImageManager defaultManager] requestImageForAsset:asset
targetSize:CGSizeMake(100.0f, 100.0f)
contentMode:PHImageContentModeAspectFit
options:imageOption
resultHandler:^(UIImage * _Nullable result, NSDictionary * _Nullable info) {
self.imageView.image = result;
}];
*stop = YES;
}];
}
-
相册列表结果展示
1.2、UICollectionView
在上述表格中我们曾介绍过PHFetchOption,它表示一个相册集合。
获取collectionView的数据源其实很简单,与上述我们在cell中获取图片封面一样,通过PHAssetCollection就能拿到。
// 照片按照时间生序排列
PHFetchOptions *option = [[PHFetchOptions alloc] init];
option.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:NO]];
// 获取照片结果集合,将其添加至数据源数组
PHFetchResult *result = [PHAsset fetchAssetsInAssetCollection:self.colletion options:option];
[result enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
PHAsset *asset = (PHAsset *)obj;
[self.assets addObject:asset];
if (idx == result.count - 1) {
[self.collectionView reloadData];
}
}];
AssetsLibrary
2.1、TableView
-
获取相册集合
/** 代表整个设备中的资源库(照片库 */
@property (nonatomic, retain) ALAssetsLibrary *assetsLibrary;
// 实例化一个对象
self.assetsLibrary = [[ALAssetsLibrary alloc] init];
// 获取相册
[self.assetsLibrary enumerateGroupsWithTypes:ALAssetsGroupAll usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
if (group) {
// 将相册图片张数不为零的添加到数据源中
if (group.numberOfAssets > 0) {
[_groups addObject:group];
}
} else {
[self.tableView reloadData];
}
} failureBlock:^(NSError *error) {
}];
-
更新cell
- (void)refresh:(ALAssetsGroup *)assetsGroup {
// 相册封面赋值
size_t height = CGImageGetHeight(assetsGroup.posterImage);
float scale = height / 60.0f;
UIImage *groupImage = [UIImage imageWithCGImage:assetsGroup.posterImage
scale:scale
orientation:UIImageOrientationUp];
self.imageView.image = groupImage;
/*
* property
*
* ALAssetsGroupPropertyName 相册的名字
* ALAssetsGroupPropertyType 相册的类型
* ALAssetsGroupPropertyPersistentID 相册的存储id
* ALAssetsGroupPropertyURL 相册存储的位置地址
*/
self.textLabel.text = [assetsGroup valueForProperty:ALAssetsGroupPropertyName];
// 相册图片张数
self.detailTextLabel.text = [NSString stringWithFormat:@"%ld", (long)[assetsGroup numberOfAssets]];
self.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
-
信息对照表
属性 | 描述 |
---|---|
AssetsLibrary | 代表整个设备中的资源库(照片库),通过 AssetsLibrary 可以获取照片和视频 |
ALAssetsGroup | 通过 ALAssetsGroup 可以获取某个相册的信息,相册下的资源,同时也可以对某个相册添加资源 |
ALAsset | 映射照片库中的一个照片或视频,通过 ALAsset 可以获取某个照片或视频的详细信息,或者保存照片和视频 |
ALAssetRepresentation | 对 ALAsset 的封装(但不是其子类),可以更方便地获取 ALAsset 中的资源信息 |
-
枚举类型
enum {
ALAssetsGroupLibrary // 本地和 iTunes
ALAssetsGroupAlbum // 从iTunes同步来的照片,不包括共享的(例如从各个软件中保存下来的图片)
ALAssetsGroupEvent // 同步到 iTunes 的(包括相机导入的)
ALAssetsGroupFaces // 同步 iTunes 的
ALAssetsGroupSavedPhotos // 相机胶卷
ALAssetsGroupPhotoStream // 照片流
ALAssetsGroupAll // 全部相册
};
-
collectionView
/*
* 将相册中的图片放入collectionView数据源中
*
* NSEnumerationConcurrent 正序遍历
* NSEnumerationReverse 反向遍历
*/
[self.assetsGroup enumerateAssetsWithOptions:NSEnumerationReverse usingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop) {
if (result) {
if ([[result valueForProperty:ALAssetPropertyType] isEqual:ALAssetTypePhoto]) {
//只访问照片,不访问视频
[self.assets addObject:result];
}
} else {
[self.collectionView reloadData];
}
}];
后续还会整理一下拍照和视频录制的一些内容,欢迎点赞
参考地址:
iOS 开发之照片框架详解
Photos 框架实践以及坑