简介
Photos framework是iOS8苹果提供的新的图片框架,能直接获取图片和视频,包括iCloud上面的图库。使用这个框架可以获取assets来展示和回放,编辑图片或者视频内容,或者使用系统相册、时刻、和分享到iCloud的相册来进行相关操作,本文侧重相片的获取与保存。
demo地址在这里
新特性与一些概念
获取实体&改变请求:
Photos framework中的三个model类:PHAsset
,PHAssetCollection
,PHCollectionList
,分别对应三个类型的实体:asset
(图片和视频),assets
集合(相册和时刻),集合列表(相册文件夹或者moment clusters)。这些对象,也成为 相片实体(photo entities),只包含元数据,同时也是read-only
属性。改变观察机制:
使用这个特性能够在其他app、设备更改了图片和视频内容或者相册的时候通知你的app,在实现观察机制前需要用PHPhotoLibrary
对象先为你获取到的照片实体注册一个观察者支持手机中的照片app的特性
使用PHCollectionList
类查找获取照片中响应的时刻层级asset,使用PHAsset
标记连拍照片,全景照片和高分辨率视频等。当开启iCloud图库后,Photo framework能从iCloud中获取到使用相同apple ID登陆的不同设备中的照片。加载缓存asset和缩略图
使用PHImageManager
请求asset中的特定尺寸的图片或者通过AV Foundation 对象获取video asset。Photos frame会根据设定自动下载,缓存,以便于高效复用。对于大量的assets的快速执行,例如在生成相册中所有图片的缩略图时,使用PHCachingImageManager
的子类来实现。编辑asset内容
PHAsset
和PHAsssetChangeRequest
定义了一些用于编辑照片或者视频内容的方法,同时允许将编辑后的图片保存到图库中。为了可以让用户可以在前次修改的基础上再次修改,或者是在不同的app上进行多次修改,Photos保存了当前和县区版本的asset,如果单独使用PHAdjustMnentData
对象只能获取到最后编辑的照片。如果你的app支持在前面修改基础上多次编辑,你应该允许用户撤回或者修改本次编辑。
framework中的类
PHAdjustmentData
当修改了一个asset后,Photos会将PHAdjustmentData
对象和修改过的图片或者视频数据一起保存起来,你可以通过这个对象提供的一些数据来修改已经保存的编辑。
当需要调整data的时候,调用
- (PHContentEditingInputRequestID)requestContentEditingInputWithOptions:(PHContentEditingInputRequestOptions *)options
completionHandler:(void (^)(PHContentEditingInput *contentEditingInput, NSDictionary *info))completionHandler
初始化方法:
/**
* 用于标记特定的修改数据,使用formatIndentifier和formatVersion指定唯一的adjustment,
* data存储用于回滚到之前状态的数据,也就是当前图片的修改数据,以便于在这个基础上继续进行修改
*
* @param formatIndentifier 用于标记 adjustment data
* @param formatVersion version number for adjustment data
* @param data 包含了重新修改需要的数据
*
*/
- (instanceType)initWithForamtIndentifier:(NSString *)formatIndentifier
formatVersion:(NSString *)formatVersion
data:(NSData *)data;
PHAssetChangeRequest
在图库中使用PHAssetChangeRequest
对象来创建,修改,删除PHAsset
对象。
可以使用适当的类方法来实现增加删除修改asset的目的:
/**
* 删除给定的assets
*
* @param assets 包含需要删除的asset数组
*/
+ (void)deleteAssets:(id<NSFastEnumeration>)assets;
/**
* 修改给定的PHAsset对象
*
* 在修改之前使用 'canPerformEditOperation:' 方法判断asset是否允许修改
* 在photo library change block中创建修改请求后,设置正确的修改请求属性。
*
* @param asset 待修改的asset
*/
+ (instancetype)changeRequestForAsset:(PHAsset *)asset;
PHFetchOptions
当你使用没方法获取PHAsset
(图片和视频),PHCollection
(相册类型),PHAssetCollection
(相册)的实体(相当于包含多个数据的模型)使用PHFetchOptions
对象指定操作,例如获取的实体的排列属性等。
调用PHFetchRequest
类方法创建一个包含fetch请求的对象。
这个类包含了两个属性:predicate
和sortDescriptors
。
- NSPredicate *predicate:
specifies which properties to select results by and that also specifies any constraints on selection.
用于指定返回的结果和指定条件
示例:
PHFetchOperation *fetchOptions = [PHFetchOption new];
fetchOperations.predicate = [NSPredicate predicateWithFormat:
@"(mediaSubtype & %d) != 0 || (mediaSubtype & %d) != 0",
PHAssetMediaSubtypePhotoPanorama,PHAssetMediaSubtypeVideoHightFrameRate];
PHFetchResult *result = [PHAsset fetchAssetWithOptions:fetchOptions];
- NSArray <NSSortDescriptor *> *sortDescriptors
用于指定获取的对象的顺序
用一个例子实现以时间顺序倒序(刚拍摄的照片放在前头排列获取图片的功能:
PHFetchOption *option = [PHFetchOption new];
option.sourtDescriptors = @[NSSortDescriptors sortDescriptiorWithKey:@"creationDate" ascending:NO];
PHFetchResult *result = [PHAsset fetchAssetsWithOptions:option];
其他几个属性:
//用于确定app是否接收到了具体的改变信息。
@property(nonatomic, assign) BOOL wantsIncrementalChangeDetails
/**
*限制检索获取得到的photo实体的最小数量,当实体数量十分巨大时,作用显著,
*例如,用'PHAsset'的'fetchAssetsWithOptions:'方法获取最近拍摄的照片等。
*/
@property(nonatomic, assign, readwrite) NSUInteger fetchLimit
//检索结果是否包含连拍图片,如果是NO,只显示用户选取的那种图片,如果为YES,
@property(nonatomic, assign) BOOL includeAllBurstAssets
//顾名思义,是否检索隐藏的图片
@property(nonatomic, assign) BOOL includeHiddenAssets
/**
*包含的数据类型:
*PHAssetSourceTypeUserLibrary
*PHAssetSourceTypeCloudShared
*PHAssetSourceTypeiTunesSynced
*/
@property(nonatomic, assign) PHAssetSourceType includeAssetSourceTypes
PHImageRequestOption
PHImageRequestOption 用于配置从PHImageManager中获取asset的请求。
常用的一些属性和方法有:
/**
* @discuss 如果是NO,表示为异步操作,从manager中请求图片时,
* 'reqeustImageForAssets:targetSize:options:resultHandler'方法会立即返回,
* 建议在后台只在后台线程中执行同步请求
*/
@property (nonatomic, assign) BOOL synchronous;
/**
*typedef : NSInteger {
PHImageRequestOptionsDeliveryModeOpportunistic = 0,
PHImageRequestOptionsDeliveryModeHighQualityFormat = 1,
PHImageRequestOptionsDeliveryModeFastFormat = 2,
} PHImageRequestOptionsDeliveryMode;
*@PHImageRequestOptionsDeliveryModeOpportunistic:
自动返回一个或多个结果,来平衡图片的质量和响应性,它会多次调用
resutltHandler:'requestImageForAssets:targetSize:options:resultHandler'.
它首先会先调用一次'resutltHandler'返回清晰度低的图片作为临时显示图片,
等到能获取到高清晰度的图片时再次调用'resutltHandler'返回高质量图片。
note:如果'synchronous'是NO那么该属性无效
*@PHImageRequestOptionsDeliveryModeHighQualityFormat
只返回高分辨率的图片。具有较高的优先权
*@PHImageRequestOptionsDeliveryModeFastFormat
快速返回结果,可能会牺牲图片质量。
*/
@property (nonatomic, assign) PHImageRequestOptionsDeliveryMode deliveryMode;
/**
* a mode that specifies how to resize the reuqested image
*/
@property(nonatomic, assign) PHImageRequestOptionsResizeMode resizeMode;
从iCloud中下载照片
/**
* 能否从iCloud中下载图片,
YES,本地也没有保存此图片,从iCloud上下载,用'progressHandler'这个block获取下载进度;
NO,同时本地也没有图片,'progressHander'中的info[PHImageReustIsInCloudKey]返回值会表示不能下载
*/
@property(nonatomic, assign, getter=isNetworkAccessAllowed) BOOL networkAccessAllowed;
/**
下载进度block,在多条线程中执行操作,刷新UI时需要回到主线程
*/
typedef void (^ PHAssetImageProgressHandler)(double progress, NSError *error, BOOL *stop, NSDictionary *info);
@property(nonatomic, copy) PHAssetImageProgressHandler progressHandler
PHFetchResult
PHFetchRequest
是有序的photo实体对象的容器,包含通过给定的检索条件返回的asset,相册,一个相册类型中的所有相册列表(例如,smart album类型下的所有相册,它是有序的),在PHAsset
,PHCollection
,PHAssetcollection
,PHCollectionList
这几个类中都包含有相应的类方法包含对应信息的PHFetchRequest
对象,例如:
- PHAsset
/**
* @param mediaType
* aaset类型,包含有:
* PHAssetMediaTypeUnknown = 0,
* PHAssetMediaTypeImage,
* PHAssetMediaTypeVideo,
* PHAssetMediaTypeAudio,
*
* @param options 检索操作
*
* @return 带有符合条件的PHAsset对象集合的PHFetchRequest对象
*/
+(PHFetchResult<PHAsset *> *)fetchAssetsWithMeidaType:(PHAssetMediaType)mediaType
options:(PHFetchOptions *)options
- PHCollection (抽象类,不允许直接使用,开发过程中用它的两个子类PHCollectionList和PHAssetCollection替代)
//Retrieves collections from the root of the photo library’s hierarchy of user-created albums and folders.
+(PHFetchResult<PHCollection *> *)fetchTopLevelUserCollectionsWithOption:(PHFetchOptions *)options;
- PHAssetCollection:
/**
* 获取自定条件的相册
*
* @param type 相册类型:
PHAssetCollectionTypeAlbum
PHAssetCollectionTypeSmartAlbum
PHAssetCollectionTypeMoment
* @param subtype 相对于相册类型更具体些的相片类型,如全景,照片流等
* @param options 检索条件
*
* @return 符合条件的相册
*/
+(PHFetchResult<PHAssetCollection *> *)fetchAssetColletcionsWithType:(PHAssetCollectionType)type
subtype:(PHAssetCollectionSubtype)subtype
options:(PHFetchOptions *)options;
- PHCollectionList:
+(PHFetchResult<PHCollectionList *> *)fetchCollectionListsWithType:(PHCollectionListType)collectionListType
subtype:(PHCollectionListSubtype)subtype
options:(PHFetchOptions *)options
PHFetchRequest虽然包含了符合条件的photo实体的集合,与NSArray不同的是它会动态改变包含的内容,在处理大量的asset的时候效率更高些,实现的效果也好点
常用属性与方法
//是否包含给定的对象
- (BOOL)containsObject:(ObjectType)anObject
//包含的photo实体个数
@property(readonly) NSUInteger count
//指定类型的asset数量
- (NSUInteger)countOfAssetsWithMediaType:(PHAssetMediaType)mediaType
PHImageManager
PHImageManager有一个单例方法,同时也提供了用于获取全尺寸图片,图片缩略图等方法.
获取图片:
/**
* 会多次调用'resultHandler',第一次调用,返回的图片清晰度较低,
* 当高清晰度图片可以获取时,会再次调用,如果高质量的图片在缓存汇中,只调用一次'resultHandler'。
* 这个方法默认是异步的,当从后台线程调用这个方法时,需要将options的'synchronous'设为YES.
*
* @param asset 保存图片信息的asset
* @param targetSize 返回的图片尺寸
* @param contentMode 返回的图片显示模式
* @param options image request option
* @param resultHandler 返回的内容
*
* @return 请求标示,用于取消请求*/
- (PHImageRequestID)requestImageForAsset:(PHAsset *)asset
targetSize:(CGSize)targetSize
contentMode:(PHImageContentMode)contentMode
options:(PHImageRequestOptions *)options
resultHandler:(void (^)(UIImage *result, NSDictionary *info))resultHandler;
/**
* 请求全尺寸的图片,只执行一次'resultHandler',
* 如果options的'version'被设置为'PHImageRequestOptionsVersionCurrent,'
* 返回在这个asset上发生的任何编辑后的imageData,如果不是的话,返回最原始版本的图片
*
* @param asset 保存图片信息的asset
* @param options image request option
* @param resultHandler 返回的内容
*
* @return 请求标示,用于取消请求
*/
- (PHImageRequestID)requestImageDataForAsset:(PHAsset *)asset
options:(PHImageRequestOptions *)options
resultHandler:(void (^)(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info))resultHandler;
取消请求:
- (void)cancelImageRequest:(PHImageRequestID)requestID
PHCachingImageManager
PHCachingImageManager是PHImageManager的子类,如果相册中有大量的图片,而你的需求是要快速的获取这些图片的缩略图和大图数据用于展示,这个时候可以用PHCachingImageManager来实现,它具有缓存机制可以快速获取一个图册的缩略图,或者在后台请求全尺寸图片以便于快速展示。
开始缓存图片:
/**
* @discuss 当调用这个方法的时候,cacaingManager开始在获取你所需要的数据,
* 同时在后台生成缩略图,之后可以使用'requestImagesFromAssets:targetSize:contentMode:resultHandler:'
* 方法从缓存中请求数据。
* Photos会按照给定的尺寸,显示模式返回图片,缓存中的图片尺寸是固定的
*
* @param assets 需要缓存的assets
* @param targetSize 图片尺寸
* @param contentMode 显示模式
* @param options 请求操作
*/
- (void)startCachingImagesForAssets:(NSArray *)assets
targetSize:(CGSize)targetSize
contentMode:(PHImageContentMode)contentMode
options:(PHImageRequestOptions *)options;
取消图片缓存:
/**
* 当使用collectionView展示图片缩略图的时候,如果需要改变尺寸大小的时候,
* 就需要将缓存中的旧图片删除重新缓存。
*
* @param assets 需要缓存的assets
* @param targetSize 图片尺寸
* @param contentMode 显示模式
* @param options 请求操作
*/
- (void)stopCachingImagesForAssets:(NSArray *)assets
targetSize:(CGSize)targetSize
contentMode:(PHImageContentMode)contentMode
options:(PHImageRequestOptions *)options;
PHPhotoLibrary
PHPhotoLibrary可以看成是一个用户图库,包含了一些的图片和相册,同时包含本地的和iCloud中的资源。当Photos app发生图片的修改、增加、删除等改变时,使用PHPhotoLibrary来做一些刷新UI,保存数据等响应动作,同时也可以注册观察者(使用registerChangeObserver
方法),当Photos app内容发生改变的时,会触发代理方法photoLibraryDidChange
。
使用PHPhotoLibrary响应图库改变
Photos中的PHAsset,PHAssetCollection等是不可变对象,因此要改变当前这些类型对象的时候就需要使用photo library实现改变。
请求改变数据需要用到PHAssetChangeRequest
,PHAssetcCollectionChangeRequest
,PHCollectionListChangeRequest
。
使用步骤:
1、创建实体
每个change request类都提供了用于创建响应的实体类的方法:
creationRequestForAssetCollectionWithTitle:
创建了一个asset Collection
如果要添加一个新的asset到collection中,需要用到change request提供的PHObjectPlaceholder
对象。在change block结束之后,使用placeholder对象提供的localIdentifier
属性获取创建好的asset对象。
2、删除实体
例如:deleCollectionLists
用于删除collection list
3、修改实体
changeRqeustForAsset
创建了一个可用于修改的asset的change request
下面代码实现往相册中添加新图片的功能:
- (void)addNewAssetWithImge:(UIImage *)image toAlbum:(PHAssetCollection *)album {
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
PHAssetChangeRequest *assetChange = [PHAssetChangeRequest creationRequestForAssetFromImage:image];
PHAssetCollectionChangeRequest *collectionChange = [PHAssetCollectionChangeRequest changeRequestForAssetCollection:album];
PHObjectPlaceholder *placeholder = [assetChange placeholderForCreatedAsset];
[collectionChange addAssets:@[placeholder]];
} completionHandler:^(BOOL success, NSError * _Nullable error) {
NSLog(@"Finished adding asset. %@", (success ? @"Success" : error));
}];
}
note:For each call to the performChanges:completionHandler: or performChangesAndWait:error: method, iOS shows an alert asking the user for permission to edit the contents of the photo library.
注册观察者
PHPhotoLibraryChangeObserver
在系统图库发生变化的时候就会通知指定的观察者,只要是使用了Photos framework实现的改变,都会触发观察者的方法。
registerChangeObserver:
方法指定一名观察者,当系统图库发生改变,会触发观察者的指定方法,这个方法在PHPhotoLibraryChangeObserver
协议中:- (void)photoLibraryDidChange:(PHChange *)changeInfo ;
PHCollection
PHCollection是一个抽象类,不允许直接使用它的实例对象,而是使用它的两个子类:PHAssetCollection
,PHCollectionList
:
PHAssetCollection 包含了带有图片或者视频信息PHAsst对象,PHCollectionList可以看做是一个文件夹,里面存放多个相册或者是在年度相册中的所有时刻的照片。
获取图片集合:
//用特定的操作从collection list中获取collection集合
+ (PHFetchResult<PHCollection *>)fetchCollectionsInCollectionList:(PHCollectionList *)collectionList options:(PHFetchOptions *)options
//获取所有用户创建的相册或者文件夹。
+ (PHFetchResult<PHCollection *> *)fetchTopLevelUserCollectionWithOptions:(PHFetchOptions *)options;
PHAssetCollection
一个PHAssetCollection代表一个相册,可能包含图片和视频,例如:时间相册,我的照片流,自拍。
在Photos framework中不能直接获取collection中的内容,通过fetchAssetsInAssetCollecton:options:
获取到包含PHAsset集合的PHFecthReult对象,之后用fetchResult提供的方法取出图片或者视频。
获取asset collection 的方法有许多种:
/**
* @param type 相册类型:
PHAssetCollectionSubtypeAlbumRegular
Photos APP中创建的相册
PHAssetCollectionSubtypeAlbumSyncedEvent
从iPhoto中同步的事件相册
PHAssetCollectionSubtypeAlbumSyncedFaces
从iPhoto中同步的包含人脸的相册
PHAssetCollectionSubtypeAlbumSyncedAlbum
从iPhoto同步的普通相册
PHAssetCollectionSubtypeAlbumImported
从其他设备导入的相册
PHAssetCollectionSubtypeAlbumCloudShared
分享到iCloud的照片流相册
PHAssetCollectionSubtypeAlbumMyPhotoStream
用户个人的iCloud照片流
PHAssetCollectionSubtypeSmartAlbumGeneric
没有特定类型的只能相册
PHAssetCollectionSubtypeSmartAlbumPanoramas
全景图片相册
PHAssetCollectionSubtypeSmartAlbumVideos
视频相册
PHAssetCollectionSubtypeSmartAlbumFavorites
个人收藏
PHAssetCollectionSubtypeSmartAlbumTimelapses
延时摄影相册
PHAssetCollectionSubtypeSmartAlbumAllHidden
隐藏的图片相册
PHAssetCollectionSubtypeSmartAlbumRecentlyAdded
最近添加的图片
PHAssetCollectionSubtypeSmartAlbumBursts
连拍相册
PHAssetCollectionSubtypeSmartAlbumSlomoVideos
慢动作视频相册
PHAssetCollectionSubtypeSmartAlbumUserLibrary
在本地存在的相册(不包含icloud中的相册)
PHAssetCollectionSubtypeSmartAlbumSelfPortraits
自拍
PHAssetCollectionSubtypeSmartAlbumScreenshots
截屏
PHAssetCollectionSubtypeAny
获取所有类型的相册
* @param options 筛选操作
*
* @return 包含符合条件的相册的fetch result
*/
+ (PHFetchResult<PHAssetCollection *> *)fetchAssetCollectionsWithType:(PHAssetCollectionType)type subtype:(PHAssetCollectionSubtype)subtype options:(nullable PHFetchOptions *)options;
/**
* 获取符合特定类型同时包含指定asset的相册集合
*
* @param asset 给定的sset
* @param type 指定相册类型
* @param options 获取操作
*
*/
+ (PHFetchResult<PHAssetCollection *> *)fetchAssetCollectionsContainingAsset:(PHAsset *)asset withType:(PHAssetCollectionType)type options:(PHFetchOptions *)options;
PHCollectionList
PHCollectionList包含了更高级别的asset集合,可嵌套PHAssetCollection
和自身类型,还支持多重嵌套,例如获取时刻相册和时刻相册中年度照片等。
获取collection list的几种方法:
+ (PHFetchResult<PHCollectionList *>)fetchCollectionListsContainingCollection:(PHCollection *)collection options:(PHFetchOptions *)options;
+ (PHFetchResult<PHCollectionList *> *)fetchCollectionListsContainingCollection:(PHCollection *)collection options:(nullable PHFetchOptions *)options;
+ (PHFetchResult<PHCollectionList *> *)fetchCollectionListsWithLocalIdentifiers:(NSArray<NSString *> *)identifiers options:(nullable PHFetchOptions *)options;
+ (PHFetchResult<PHCollectionList *> *)fetchCollectionListsWithType:(PHCollectionListType)collectionListType subtype:(PHCollectionListSubtype)subtype options:(nullable PHFetchOptions *)options;
+ (PHFetchResult<PHCollectionList *> *)fetchMomentListsWithSubtype:(PHCollectionListSubtype)momentListSubtype containingMoment:(PHAssetCollection *)moment options:(nullable PHFetchOptions *)options;
+ (PHFetchResult<PHCollectionList *> *)fetchMomentListsWithSubtype:(PHCollectionListSubtype)momentListSubtype options:(nullable PHFetchOptions *)options;
PHAsset
PHAsset代表一个视频或者图片资源,当需要展示或者修改Photos app中的图片时需要先获取asset,asset是不可改变的并且只保存了所代表的视频或者图片的metadata。
获取PHAsset的几种方法:
//从asset collection中获取符合条件的asset集合
+ (PHFetchResult <PHAsset *> *)fetchAssetsInAssetCollection:(PHAssetCollection *)assetCollection options:(PHFetchOptions *)options
/**
* 默认返回所有类型的asset,如果要给asset添加更多的类型限定,可以在options的filter predicate中设置。
* @param mediaType :
PHAssetMediaTypeUnknown
未知类型
PHAssetMediaTypeImage
静态图片
PHAssetMediaTypeVideo
视频
PHAssetMediaTypeAudio
音频
注: PHMediaSubtype:
PHAssetMediaSubtypeNone = 0,
// Photo subtypes
PHAssetMediaSubtypePhotoPanorama = (1UL << 0),
PHAssetMediaSubtypePhotoHDR = (1UL << 1),
PHAssetMediaSubtypePhotoScreenshot NS_AVAILABLE_IOS(9_0) = (1UL << 2),
PHAssetMediaSubtypePhotoLive NS_AVAILABLE_IOS(9_1) = (1UL << 3),
// Video subtypes
PHAssetMediaSubtypeVideoStreamed = (1UL << 16),
PHAssetMediaSubtypeVideoHighFrameRate = (1UL << 17),
PHAssetMediaSubtypeVideoTimelapse = (1UL << 18),
*/
+ (PHFetchResult <PHAsset *> *_Nullable)fetchAssetsWithMediaType:(PHAssetMediaType)mediaType options:(PHFetchOptions *_Nullable)options;
+ (PHFetchResult *<PHAsset *> *)fetchAssetsWithLocalIndentifiers:(NSArray <NSString *>)identifier options:(PHFetchOptions *)options;
/**
* 获取key asset,每一个collection都至少有一个key asset,每个不同类型的collection用于定义key asset的方式也不同,有的collection中只有一个key asset,有的含有多个。
* 例如在相机胶卷中,最近拍摄的图片或者视频就是key asset
*/
+ (PHFetchResult <PHAsset *>)fetchKeyAssetsInAssetCollection:(PHAssetCollection *)collection options:(PHFetchOptions *)options;
/**
* 获取所有的符合条件的asset,默认条件下,不包含通过iTunes同步过来的和存储在iCloud中的图片。
* 更改此条件,配置options的'includeAssetSourceType'属性来实现。
*/
+ (PHFetchResult <PHAsset *> *)fetchAssetsWithOptions:(PHFetchOptions *)options;
//获取连拍照片
+ (PHFetchResult <PHAsset *>)fetchAssetWithBurstIdentifier(NSString *)burstIdentifier options:(PHFetchOptions *)options;