由于前段时间,需要完成一个跟相册的需求,所以阅读了一些与图片有关的文章,和使用了一些相关的第三方库。
在 iOS 设备中,照片和视频是相当重要的一部分。在 iOS 8 出现之前,开发者只能使用 AssetsLibrary
框架来访问设备的照片库,这是一个有点跟不上 iOS 应用发展步伐以及代码设计原则但确实强大的框架,考虑到 iOS7 仍占有不少的渗透率,因此 AssetsLibrary
也是本文重点介绍的部分。随着 iOS 8 的到来,苹果给我们提供了一个现代化的框架 —— PhotoKit
,它比 AssetsLibrary
表现更好,并且拥有让应用和设备照片库无缝工作的特性。
另外值得强调的是,在 iOS 中,照片库并不只是照片的集合,同时也包含了视频。在 AssetsLibrary
中两者都有相同类型的对象去描述,只是类型不同而已。文中为了方便,大部分时候会使用「资源」代表 iOS 中的「照片和视频」。
PhotoKit 对象模型
PhotoKit
定义了与系统的 Photos 应用内展现给用户的模型对象相一致的实体图表。这些照片实体都是轻量级的不可变对象。所有的 PhotoKit
对象都是继承自 PHObject
抽象基类,其公共接口只提供了一个 localIdentifier
属性。
PHAsset
表示用户照片库中一个单独的资源,用以提供资源的元数据。
成组的资源叫做资源集合,用 PHAssetCollection
类表示。一个单独的资源集合可以是照片库中的一个相册或者一个时刻,或者是一个特殊的“智能相册”。这种智能相册包括所有的视频集合,最近添加的项目,用户收藏,所有连拍照片等等。PHAssetCollection
是 PHCollection
的子类。
PHCollectionList
表示一组的 PHCollections
。因为它本身就是 PHCollection
,所以集合列表可以包含其他集合列表,它们允许复杂的集合继承。实际上,我们可以在照片应用的时刻栏目中看到它:照片 --- 时刻 --- 精选 --- 年度,就是一个例子。
获取 (Fetch) 照片实体
获取 vs. 枚举
那些熟悉 AssetsLibrary
框架的开发者可能会记得 AssetsLibrary
可以用一些特定属性来找到需要的资源,其中一个必须枚举用户资源库来获得匹配的资源。不得不承认,这个 API 虽然提供了一些缩小搜索域的方法,但还是十分低效。
而与之形成鲜明对比,PhotoKit
实体的实例是通过获取得到的。那些熟悉 Core Data
的人,会觉得和 PhotoKit
在概念和描述都比较接近。
AssetsLibrary
的组成比较符合照片库本身的组成,照片库中的完整照片库对象、相册、相片都能在 AssetsLibrary
中找到一一对应的组成,这使到 AssetsLibrary
的使用变得直观而方便。
AssetsLibrary: 代表整个设备中的资源库(照片库),通过 AssetsLibrary
可以获取和包括设备中的照片和视频
- ALAssetsGroup: 映射照片库中的一个相册,通过
ALAssetsGroup
可以获取某个相册的信息,相册下的资源,同时也可以对某个相册添加资源。 - ALAsset: 映射照片库中的一个照片或视频,通过
ALAsset
可以获取某个照片或视频的详细信息,或者保存照片和视频。 - ALAssetRepresentation:
ALAssetRepresentation
是对ALAsset
的封装(但不是其子类),可以更方便地获取 ALAsset 中的资源信息,每个 ALAsset 都有至少有一个ALAssetRepresentation
对象,可以通过defaultRepresentation
获取。而例如使用系统相机应用拍摄的 RAW + JPEG 照片,则会有两个ALAssetRepresentation
,一个封装了照片的 RAW 信息,另一个则封装了照片的 JPEG 信息。
获取请求
获取操作是由上面描述的实体的类方法实现的。要使用哪个类/方法,取决于问题所在范围和你展示与遍历照片库的方式。所有获取方法的命名都是相似的:class func fetchXXX(..., options: PHFetchOptions) -> PHFetchResult 。options 参数给了我们一个对结果进行过滤和排序的途径,这和 NSFetchRequest
的 predicate
与 sortDescriptors
参数类似。
获取结果
你可能已经注意到了这些获取操作不是异步的。它们返回了一个 PHFetchResult
对象,可以用类似 NSArray 的接口来访问结果内的集合。它会按需动态加载内容并且缓存最近请求的内容。这个行为和设置了 batchSize
属性的 NSFetchRequest
返回的结果数组相似。对于 PHFetchResult
来说,没有办法用参数来指定这个行为,但是官网文档保证 “即使在处理大量的返回结果时,依然能够有最好的表现”。
PHImageManager 照片加载
在处理用户照片库的过去几年中,开发者创造了上百 (如果没有上千) 的小技巧来提高照片加载和展示的效率。这些技巧处理请求的派发和取消,图像大小的修改和裁剪,缓存等等。PhotoKit 提供了一个可以用更加便捷和现代的 API 做了所有这些操作的类:PHImageManager
。
- (PHImageRequestID)requestImageForAsset:(PHAsset *)asset targetSize:(CGSize)targetSize contentMode:(PHImageContentMode)contentMode options:(nullable PHImageRequestOptions *)options resultHandler:(void (^)(UIImage *__nullable result, NSDictionary *__nullable info))resultHandler;
- (PHImageRequestID)requestImageDataForAsset:(PHAsset *)asset options:(nullable PHImageRequestOptions *)options resultHandler:(void(^)(NSData *__nullable imageData, NSString *__nullable dataUTI, UIImageOrientation orientation, NSDictionary *__nullable info))resultHandler;
PHImageRequestOptions 控制加载图片时的参数
提供了一些方式来确定图像管理器该以怎样的方式来重新设置图像大小。
resizeMode
属性可以设置为 .Exact
(返回图像必须和目标大小相匹配),.Fast
(比 .Exact 效率更高,但返回图像可能和目标大小不一样) 或者 .None
。
还有个值得一提的是,normalizedCroppingMode
属性让我们确定图像管理器应该如何裁剪图像。注意:如果设置了 normalizedcroppingMode
的值,那么 resizeMode
需要设置为 .Exact
。
结果回调 (result handler)
结果回调是一个包含了一个 UIImage 变量和一个 info 字典作为参数的 block。根据参数和请求的选项,在请求的整个生命周期,它可以被图像管理器多次调用。
info
字典提供了关于当前请求状态的信息,比如:
图像是否必须从 iCloud 请求 (如果你初始化时将 networkAccessAllowed 设置成 false,那么就必须重新请求图像) —— PHImageResultIsInCloudKey
。
当前递送的 UIImage 是否是最终结果的低质量格式。当高质量图像正在下载时,这个可以让你给用户先展示一个预览图像 —— PHImageResultIsDegradedKey
。
请求 ID (可以便捷的取消请求),以及请求是否已经被取消 —— PHImageResultRequestIDKey
和 PHImageCancelledKey
。
如果没有图像提供给 result handler,字典内还会有一个错误信息 —— PHImageErrorKey
。
TZImagePickerController(1.5.0)使用方法
方法一:
TZImagePickerController *imagePickerVc = [[TZImagePickerController alloc] initWithSelectedAssets:_selectedAssets selectedPhotos:_selectedPhotos index:indexPath.row];
imagePickerVc.allowPickingOriginalPhoto = self.allowPickingOriginalPhotoSwitch.isOn;
imagePickerVc.isSelectOriginalPhoto = _isSelectOriginalPhoto;
[imagePickerVc setDidFinishPickingPhotosHandle:^(NSArray<UIImage *> *photos, NSArray *assets, BOOL isSelectOriginalPhoto) {
_selectedPhotos = [NSMutableArray arrayWithArray:photos];
_selectedAssets = [NSMutableArray arrayWithArray:assets];
_isSelectOriginalPhoto = isSelectOriginalPhoto;
_layout.itemCount = _selectedPhotos.count;
[_collectionView reloadData];
_collectionView.contentSize = CGSizeMake(0, ((_selectedPhotos.count + 2) / 3 ) * (_margin + _itemWH));
}];
方法二:
首先遵守<TZImagePickerControllerDelegate>
TZImagePickerController *imagePickerVc = [[TZImagePickerController alloc] initWithMaxImagesCount:maxSelectCount delegate:self];
imagePickerVc.allowPickingOriginalPhoto = self.allowPickingOriginalPhotoSwitch.isOn;
imagePickerVc.isSelectOriginalPhoto = _isSelectOriginalPhoto;
imagePickerVc.sortAscendingByModificationDate = NO;
[self.navigationController presentViewController:imagePickerVc animated:YES completion:nil];
- (void)imagePickerController:(TZImagePickerController *)picker didFinishPickingPhotos:(NSArray<UIImage *> *)photos sourceAssets:(NSArray *)assets isSelectOriginalPhoto:(BOOL)isSelectOriginalPhoto {
imagePickerVc.sortAscendingByModificationDate = NO;
imagePickerVc.photoWidth = 1024.0;
imagePickerVc.photoPreviewMaxWidth = 3072.0;
[self.navigationController presentViewController:imagePickerVc animated:YES completion:nil];
}
注意:
1.在使用时,如果你不获取原图的话,TZImagePickerController的代理方法或block回调里返回的图片是质量非常差的图片,压缩程度非常高。因为它是直接把PHImageResultIsDegradedKey
里的图片返回,从字面上我们就可以看出,这是苹果返回的一个退化的图片,我在前面也说了,这只是返回的一个预览图像,并且TZImagePickerController在创建PHImageRequestOptions
的时候,resizeMode
使用的是PHImageRequestOptionsResizeModeFast
;如果把resizeMode
修改成PHImageRequestOptionsResizeModeNone
,可以适当的提高。
2.在外部设置photoPreviewMaxWidth
超出500~800无效,因为TZImagePickerController在内部设置了范围。
- (void)setPhotoPreviewMaxWidth:(CGFloat)photoPreviewMaxWidth {
_photoPreviewMaxWidth = photoPreviewMaxWidth;
if (photoPreviewMaxWidth > 800) {
_photoPreviewMaxWidth = 800;
} else if (photoPreviewMaxWidth < 500) {
_photoPreviewMaxWidth = 500;
}
[TZImageManager manager].photoPreviewMaxWidth = _photoPreviewMaxWidth;
}
所以,如果需要设置的话注意一下。
由于长时间没有去跟进 好多小伙伴的问题不能解决,万分抱歉,大家有问题 可以提问,可能正好碰到有解决的 帮忙解答了,/::D/::D