简介
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*)optionscompletionHandler:(void(^)(PHContentEditingInput *contentEditingInput, NSDictionary *info))completionHandler
初始化方法:
/** * 用于标记特定的修改数据,使用formatIndentifier和formatVersion指定唯一的adjustment, *data存储用于回滚到之前状态的数据,也就是当前图片的修改数据,以便于在这个基础上继续进行修改* * @param formatIndentifier 用于标记 adjustmentdata* @param formatVersion version number for adjustmentdata* @paramdata包含了重新修改需要的数据* */- (instanceType)initWithForamtIndentifier:(NSString*)formatIndentifier formatVersion:(NSString*)formatVersiondata:(NSData*)data;
PHAssetChangeRequest
在图库中使用PHAssetChangeRequest对象来创建,修改,删除PHAsset对象。
可以使用适当的类方法来实现增加删除修改asset的目的:
/**
* 删除给定的assets
*
* @param assets 包含需要删除的asset数组
*/+ (void)deleteAssets:(id)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 = [PHFetchOptionnew];fetchOperations.predicate = [NSPredicatepredicateWithFormat:@"(mediaSubtype & %d) != 0 || (mediaSubtype & %d) != 0", PHAssetMediaSubtypePhotoPanorama,PHAssetMediaSubtypeVideoHightFrameRate];PHFetchResult *result = [PHAssetfetchAssetWithOptions:fetchOptions];
NSArray *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)BOOLsynchronous;/**
*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)BOOLnetworkAccessAllowed;/**
下载进度block,在多条线程中执行操作,刷新UI时需要回到主线程
*/typedefvoid(^ PHAssetImageProgressHandler)(doubleprogress,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*)fetchAssetsWithMeidaType:(PHAssetMediaType)mediaType
options:(PHFetchOptions *)options
PHCollection (抽象类,不允许直接使用,开发过程中用它的两个子类PHCollectionList和PHAssetCollection替代)
//Retrieves collectionsfromthe rootofthe photo library’s hierarchyofuser-created albumsandfolders.+(PHFetchResult *)fetchTopLevelUserCollectionsWithOption:(PHFetchOptions *)options;
PHAssetCollection:
/*** 获取自定条件的相册** @paramtype相册类型:PHAssetCollectionTypeAlbumPHAssetCollectionTypeSmartAlbumPHAssetCollectionTypeMoment* @param subtype相对于相册类型更具体些的相片类型,如全景,照片流等* @param options 检索条件** @return 符合条件的相册*/+(PHFetchResult *)fetchAssetColletcionsWithType:(PHAssetCollectionType)typesubtype:(PHAssetCollectionSubtype)subtype options:(PHFetchOptions*)options;
PHCollectionList:
+(PHFetchResult *)fetchCollectionListsWithType:(PHCollectionListType)collectionListType subtype:(PHCollectionListSubtype)subtype options:(PHFetchOptions*)options
PHFetchRequest虽然包含了符合条件的photo实体的集合,与NSArray不同的是它会动态改变包含的内容,在处理大量的asset的时候效率更高些,实现的效果也好点
常用属性与方法
//是否包含给定的对象-(BOOL)containsObject:(ObjectType)anObject//包含的photo实体个数@property(readonly)NSUIntegercount//指定类型的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*)assettargetSize:(CGSize)targetSizecontentMode:(PHImageContentMode)contentModeoptions:(PHImageRequestOptions*)optionsresultHandler:(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*)assetoptions:(PHImageRequestOptions*)optionsresultHandler:(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会按照给定的尺寸,显示模式返回图片,缓存中的图片尺寸是固定的 * *@paramassets 需要缓存的assets *@paramtargetSize 图片尺寸 *@paramcontentMode 显示模式 *@paramoptions 请求操作 */- (void)startCachingImagesForAssets:(NSArray *)assetstargetSize:(CGSize)targetSizecontentMode:(PHImageContentMode)contentModeoptions:(PHImageRequestOptions *)options;
取消图片缓存:
/** * 当使用collectionView展示图片缩略图的时候,如果需要改变尺寸大小的时候, * 就需要将缓存中的旧图片删除重新缓存。 * *@paramassets 需要缓存的assets *@paramtargetSize 图片尺寸 *@paramcontentMode 显示模式 *@paramoptions 请求操作 */- (void)stopCachingImagesForAssets:(NSArray *)assetstargetSize:(CGSize)targetSizecontentMode:(PHImageContentMode)contentModeoptions:(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 { [[PHPhotoLibrarysharedPhotoLibrary]performChanges:^{ PHAssetChangeRequest *assetChange = [PHAssetChangeRequest creationRequestForAssetFromImage:image];PHAssetCollectionChangeRequest *collectionChange = [PHAssetCollectionChangeRequest changeRequestForAssetCollection:album];PHObjectPlaceholder *placeholder = [assetChange placeholderForCreatedAsset];[collectionChangeaddAssets:@[placeholder]];} completionHandler:^(BOOLsuccess, 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)fetchCollectionsInCollectionList:(PHCollectionList *)collectionListoptions:(PHFetchOptions *)options//获取所有用户创建的相册或者文件夹。+ (PHFetchResult *)fetchTopLevelUserCollectionWithOptions:(PHFetchOptions *)options;
PHAssetCollection
一个PHAssetCollection代表一个相册,可能包含图片和视频,例如:时间相册,我的照片流,自拍。
在Photos framework中不能直接获取collection中的内容,通过fetchAssetsInAssetCollecton:options:获取到包含PHAsset集合的PHFecthReult对象,之后用fetchResult提供的方法取出图片或者视频。
获取asset collection 的方法有许多种:
/** * @paramtype相册类型:PHAssetCollectionSubtypeAlbumRegularPhotosAPP中创建的相册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 *)fetchAssetCollectionsWithType:(PHAssetCollectionType)typesubtype:(PHAssetCollectionSubtype)subtype options:(nullablePHFetchOptions*)options;/** * 获取符合特定类型同时包含指定asset的相册集合 * * @param asset 给定的sset * @paramtype指定相册类型 * @param options 获取操作 * */+ (PHFetchResult *)fetchAssetCollectionsContainingAsset:(PHAsset*)asset withType:(PHAssetCollectionType)typeoptions:(PHFetchOptions*)options;
PHCollectionList
PHCollectionList包含了更高级别的asset集合,可嵌套PHAssetCollection
和自身类型,还支持多重嵌套,例如获取时刻相册和时刻相册中年度照片等。
获取collection list的几种方法:
+ (PHFetchResult)fetchCollectionListsContainingCollection:(PHCollection*)collection options:(PHFetchOptions*)options;+ (PHFetchResult *)fetchCollectionListsContainingCollection:(PHCollection*)collection options:(nullablePHFetchOptions*)options;+ (PHFetchResult *)fetchCollectionListsWithLocalIdentifiers:(NSArray *)identifiers options:(nullablePHFetchOptions*)options;+ (PHFetchResult *)fetchCollectionListsWithType:(PHCollectionListType)collectionListType subtype:(PHCollectionListSubtype)subtype options:(nullablePHFetchOptions*)options;+ (PHFetchResult *)fetchMomentListsWithSubtype:(PHCollectionListSubtype)momentListSubtype containingMoment:(PHAssetCollection*)moment options:(nullablePHFetchOptions*)options;+ (PHFetchResult *)fetchMomentListsWithSubtype:(PHCollectionListSubtype)momentListSubtype options:(nullablePHFetchOptions*)options;
PHAsset
PHAsset代表一个视频或者图片资源,当需要展示或者修改Photos app中的图片时需要先获取asset,asset是不可改变的并且只保存了所代表的视频或者图片的metadata。
获取PHAsset的几种方法:
//从asset collection中获取符合条件的asset集合+ (PHFetchResult *)fetchAssetsInAssetCollection:(PHAssetCollection *)assetCollectionoptions:(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 *_Nullable)fetchAssetsWithMediaType:(PHAssetMediaType)mediaTypeoptions:(PHFetchOptions *_Nullable)options;+ (PHFetchResult * *)fetchAssetsWithLocalIndentifiers:(NSArray )identifieroptions:(PHFetchOptions *)options;/**
* 获取key asset,每一个collection都至少有一个key asset,每个不同类型的collection用于定义key asset的方式也不同,有的collection中只有一个key asset,有的含有多个。
* 例如在相机胶卷中,最近拍摄的图片或者视频就是key asset
*/+ (PHFetchResult )fetchKeyAssetsInAssetCollection:(PHAssetCollection *)collectionoptions:(PHFetchOptions *)options;/**
* 获取所有的符合条件的asset,默认条件下,不包含通过iTunes同步过来的和存储在iCloud中的图片。
* 更改此条件,配置options的'includeAssetSourceType'属性来实现。
*/+ (PHFetchResult *)fetchAssetsWithOptions:(PHFetchOptions *)options;//获取连拍照片+ (PHFetchResult )fetchAssetWithBurstIdentifier(NSString *)burstIdentifieroptions:(PHFetchOptions *)options;