iOS Photos 框架使用
Photos
Photos 框架是iOS 8之后用于替代AssetsLibrary的一个现代化框架,几年以来,相机应用与照片应用发生了显著的变化,增加了许多新特性,Photos可以获取相册中的所有图片和视频资源,包括iCloud Photo library 和 Live Photos,并且能够异步获取并缓存缩略图和原图。
获取资源
相册中有两种资源可供获取,分别是PHAsset
和 PHCollection
,PHAsset
代表一个相册中的文件,这个文件可以是视频、音频或图片,我们可以通过PHAsset
来获取这个文件的所有信息,包括定位、创建时间、名称等等。而PHCollection
则是一个集合,PHCollection
有两个子类分别是PHAssetCollection
和PHCollectionList
,其中PHAssetCollection
代表一个相册,也就是PHAsset的集合,如自拍相册、截屏相册、智能相册等。PHCollectionList
代表一个自身的集合,也就是PHCollection
的集合,通常用来获取相册列表
获取请求
所有的获取操作都是由PHAsset和PHCollection的类方法实现的,方法如下:
//获取一个相册中的所有文件
open class func fetchAssets(in assetCollection: PHAssetCollection, options: PHFetchOptions?) -> PHFetchResult<PHAsset>
//通过PHAsset的LocalIdentifier获取PHAsset
open class func fetchAssets(withLocalIdentifiers identifiers: [String], options: PHFetchOptions?) -> PHFetchResult<PHAsset> // includes hidden assets by default
open class func fetchKeyAssets(in assetCollection: PHAssetCollection, options: PHFetchOptions?) -> PHFetchResult<PHAsset>?
//通过PHAsset的burstIdentifier获取PHAsset,burstIdentifier是连拍模式才会有的id,通过该id可以获取连拍模式的所有照片
open class func fetchAssets(withBurstIdentifier burstIdentifier: String, options: PHFetchOptions?) -> PHFetchResult<PHAsset>
// Fetches PHAssetSourceTypeUserLibrary assets by default (use includeAssetSourceTypes option to override)
open class func fetchAssets(with options: PHFetchOptions?) -> PHFetchResult<PHAsset>
//获取对应类型的PHAsset
open class func fetchAssets(with mediaType: PHAssetMediaType, options: PHFetchOptions?) -> PHFetchResult<PHAsset>
// assetURLs are URLs retrieved from ALAsset's ALAssetPropertyAssetURL
@available(iOS, introduced: 8.0, deprecated: 11.0, message: "Will be removed in a future release")
open class func fetchAssets(withALAssetURLs assetURLs: [URL], options: PHFetchOptions?) -> PHFetchResult<PHAsset>
其中fetchAssetsInAssetCollection:options:方法可以获取资源集合中的所有asset对象。每个方法中的 PHFetchOptions参数,是获取asset对象的一些配置,我们可以设置获取asset的条件,比如获取哪种资源,如何分类。获取的时候,如果该参数为空,则使用系统的默认值,当我们调用如上所示方法获取时,可以直接传nil。
// Fetch asset collections of a single type matching the provided local identifiers (type is inferred from the local identifiers)
//通过LocalIdentifier和option获取相册
open class func fetchAssetCollections(withLocalIdentifiers identifiers: [String], options: PHFetchOptions?) -> PHFetchResult<PHAssetCollection>
// Fetch asset collections of a single type and subtype provided (use PHAssetCollectionSubtypeAny to match all subtypes)
//通过相册类型获取相册
open class func fetchAssetCollections(with type: PHAssetCollectionType, subtype: PHAssetCollectionSubtype, options: PHFetchOptions?) -> PHFetchResult<PHAssetCollection>
// Smart Albums are not supported, only Albums and Moments
//获取包含对应PHAsset的相册,Smart Albums(系统分配智能的相册,PHAssetCollectionType枚举中选择)不支持
open class func fetchAssetCollectionsContaining(_ asset: PHAsset, with type: PHAssetCollectionType, options: PHFetchOptions?) -> PHFetchResult<PHAssetCollection>
// assetGroupURLs are URLs retrieved from ALAssetGroup's ALAssetsGroupPropertyURL
//通过URL获取相册,assetGroupURLs是通过AssetsLibrary获取的URLs
open class func fetchAssetCollections(withALAssetGroupURLs assetGroupURLs: [URL], options: PHFetchOptions?) -> PHFetchResult<PHAssetCollection>
//获取一个集合中PHAssetCollectionType 为moment的相册
open class func fetchMoments(inMomentList momentList: PHCollectionList, options: PHFetchOptions?) -> PHFetchResult<PHAssetCollection>
//获取所有PHAssetCollectionType为moment的相册
open class func fetchMoments(with options: PHFetchOptions?) -> PHFetchResult<PHAssetCollection>
PHCollectionList
// A PHAssetCollectionTypeMoment will be contained by a PHCollectionListSubtypeMomentListCluster and a PHCollectionListSubtypeMomentListYear
// Non-moment PHAssetCollections will only be contained by a single collection list
open class func fetchCollectionListsContaining(_ collection: PHCollection, options: PHFetchOptions?) -> PHFetchResult<PHCollectionList>
// Fetch collection lists of a single type matching the provided local identifiers (type is inferred from the local identifiers)
open class func fetchCollectionLists(withLocalIdentifiers identifiers: [String], options: PHFetchOptions?) -> PHFetchResult<PHCollectionList>
// Fetch asset collections of a single type and subtype provided (use PHCollectionListSubtypeAny to match all subtypes)
open class func fetchCollectionLists(with collectionListType: PHCollectionListType, subtype: PHCollectionListSubtype, options: PHFetchOptions?) -> PHFetchResult<PHCollectionList>
open class func fetchMomentLists(with momentListSubtype: PHCollectionListSubtype, containingMoment moment: PHAssetCollection, options: PHFetchOptions?) -> PHFetchResult<PHCollectionList>
open class func fetchMomentLists(with momentListSubtype: PHCollectionListSubtype, options: PHFetchOptions?) -> PHFetchResult<PHCollectionList>
PHAsset
PHAsset是用来获取图片和视频文件的元数据,相当于以前的 ALAsset,但比起ALAsset,PhotoKit 提供了额外的关于用户资源的元数据,而这些数据在以前使用 ALAssetsLibrary 框架中是没有办法访问,或者很难访问到。我们可以用PHAsset保存图片和视频资源对象, 然后展示或者修改它.它有几个重要属性:
mediaType
:资源类型,图片或者音频或视频
mediaSubtypes
:图片又包含全景图(Panorama)、HDR图片、屏幕截图、livePhoto ,我们可以使用照片资源的 mediaSubtypes 属性验证资源库中的图像在捕捉时是否开启了 HDR,拍摄时是否使用了相机应用的全景模式.
Creation date
创建时间
Location
定位
Favorite
布尔值,用户是否标记资源为"收藏",我们平时浏览照片或视频,在下方点❤️就表示收藏这张图。
hidden
要验证一个资源是否被用户标记为收被隐藏,只要检查 PHAsset 实例的 hidden 属性即可。
sourceType
: 资源可以来源自用户相册、iCloud、iTunes同步
representsBurst
和burstSelectionTypes
: 对于一个资源,如果其 PHAsset 的 representsBurst 属性为 true,则表示这个资源是一系列连拍照片中的代表照片 (多张照片是在用户按住快门时拍摄的)。它还有一个属性是 burstIdentifier,如果想要获取连拍照片中的剩余的其他照片,可以通过将这个值传入 fetchAssetsWithBurstIdentifier(...) 方法来获取。用户可以在连拍的照片中做标记;此外,系统也会自动用各种试探来标记用户可能会选择的潜在代表照片。这个元数据是可以通过 PHAsset 的 burstSelectionTypes 属性来访问。这个属性是用三个常量组成的位掩码:.UserPick 表示用户手动标记的资源,.AutoPick 表示用户可能标记的潜在资源,.None 表示没有标记的资源。
localIdentifier
Photos 框架中的根类PHObject只有一个公开接口localIdentifier,是对象唯一唯一标志符.PHObject实现了-isEqual 和-hash方法.可以直接使用localIdentifier属性对PHObject及其子类对象进行对比是否同一个对象。
PHAssetCollection
PHAssetCollection
是一组有序的资源集合,包括相册、moments、智能相册以及共享照片流.它的重要属性 ;
assetCollectionType
资源集合类型,比如相册或者“时刻”相册
typedef NS_ENUM(NSInteger, PHAssetCollectionType) {
PHAssetCollectionTypeAlbum = 1,
PHAssetCollectionTypeSmartAlbum = 2,
PHAssetCollectionTypeMoment = 3,
}
NS_ENUM_AVAILABLE_IOS(8_0);
assetCollectionSubtype 子类型
enum PHAssetCollectionType : Int {
case Album //从 iTunes 同步来的相册,以及用户在 Photos 中自己建立的相册
case SmartAlbum //经由相机得来的相册
case Moment //Photos 为我们自动生成的时间分组的相册
}
enum PHAssetCollectionSubtype : Int {
case AlbumRegular //用户在 Photos 中创建的相册
case AlbumSyncedEvent //使用 iTunes 从 Photos 照片库或者 iPhoto 照片库同步过来的事件。然而,在iTunes 12 以及iOS 9.0 beta4上,选用该类型没法获取同步的事件相册,而必须使用AlbumSyncedAlbum。
case AlbumSyncedFaces //使用 iTunes 从 Photos 照片库或者 iPhoto 照片库同步的人物相册。
case AlbumSyncedAlbum //做了 AlbumSyncedEvent 应该做的事
case AlbumImported //从相机或是外部存储导入的相册,完全没有这方面的使用经验,没法验证。
case AlbumMyPhotoStream //用户的 iCloud 照片流
case AlbumCloudShared //用户使用 iCloud 共享的相册
case SmartAlbumGeneric //文档解释为非特殊类型的相册,主要包括从 iPhoto 同步过来的相册。
case SmartAlbumPanoramas //相机拍摄的全景照片
case SmartAlbumVideos //相机拍摄的视频
case SmartAlbumFavorites //收藏文件夹
case SmartAlbumTimelapses //延时视频文件夹,同时也会出现在视频文件夹中
case SmartAlbumAllHidden //包含隐藏照片或视频的文件夹
case SmartAlbumRecentlyAdded //相机近期拍摄的照片或视频
case SmartAlbumBursts //连拍模式拍摄的照片
case SmartAlbumUserLibrary //这个命名最神奇了,就是相机相册,所有相机拍摄的照片或视频都会出现在该相册中,而且使用其他应用保存的照片也会出现在这里。
case Any //包含所有类型
}
estimatedAssetCount
:估算的asset数量
PHFetchOptions
predicate
: 做选择的约束条件。比如,只获取图片,不获取视频。指定 PHAssetMediaType为image.
// swift:
options.predicate = NSPredicate(format: "mediaType = %d", PHAssetMediaType.Image.rawValue)
sortDescriptors 可指定字段用来对获取结果进行排序
includeHiddenAssets
获取结果是否包括被隐藏的
includeAllBurstAssets
获取结果是否包括连拍资源
PHFetchOptions *options = [[PHFetchOptions alloc] init];
options.wantsIncrementalChangeDetails = YES;
options.includeAllBurstAssets = YES;
options.includeHiddenAssets = YES;
// 只取图片
options.predicate = [NSPredicate predicateWithFormat:@"mediaType == %d",PHAssetMediaTypeImage];
// 按时间排序
options.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:YES]];
PHFetchResult *albums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum subtype:PHAssetCollectionSubtypeSmartAlbumAllHidden options:nil];
PHFetchResult
类似数组,存储获取到asset对象集合。同步快速获取结果,即使结果集很大,框架也能保证获取速度. 因为它不会一次性将所有结果放进内存,而是按需批量加载,可以用类似 NSArray 的接口来访问PHFetchResult结果内的集合。
资源对象的增删改
API(1) 创建PHAssetChangeRequest对象。想要修改资源,需要创建一个PHAssetChangeRequest 。然后你就可以修改创建创建日期,资源位置,以及是否将隐藏资源,是否将资源看做用户收藏等。此外,你还可以从用户的库里删除资源。类似地,若要修改资源集合或集合列表,需要创建一个 PHAssetCollectionChangeRequest或 PHCollectionListChangeRequest对象。然后你就可以修改集合标题,添加或删除集合成员,或者完全删除集合。
(2) 操作的请求都要求在PHPhotoLibrary的performChanges的changeBlock中执行
(3)如果有更新UI操作,需要遵守PHPhotoLibraryChangeObserver
协议,实现photoLibraryDidChange(changeInfo: PHChange!)方法.在photoLibraryDidChange中进行UI更新操作。
PHPhotoLibrary.shared().performChanges({
//新增、修改或删除操作
}) { (success, error) in
//新增、修改或删除完成
}
创建一个新资源只要用 creationRequestForAssetFromXXX(...)工厂方法,来创建变化请求,并传入资源图像数据 (或一个 URL)。如果你需要对新建的资源做额外的修改,你可以用创建变化请求的placeholderForCreatedAsset属性。它会返回一个可用的 placeholder 来代替“真实的” PHAsset 引用.
PHPhotoLibrary
系统中PHPhotoLibrary单例对象 是用来维护用户照片库。当我们需要编辑资源对象元数据、资源内容、或者插入新的资源对象等,都可以借助通过PHPhotoLibrary单例对象执行block,block中创建我们指定的请求对象(比如PHAssetChangeRequest,PHAssetCollectionChangeRequest, PHCollectionListChangeRequest的对象)。photoLibraryDidChange(changeInfo: PHChange!)中进行.
协议PHPhotoLibraryChangeObserver
PHPhotoLibraryChangeObserver协议能让我们知道照片相册库中的改变。Photos会发送系统图片改变的消息,我们可以遵守PHPhotoLibraryChangeObserver协议,并通过 PHPhotoLibrary的registerChangeObserver方法将对象注册为观察者,时时接收照片改变的消息。
PHChange
PHphoto框架会提供给我们PHChange对象,我们可以调用changeDetailsForObject或者changeDetailsForFetchResult 方法,它返回给我们一个PHObjectChangeDetails对象,是对最新的照片实体对象的引用,可以告诉我们对象的图像数据是否曾变化过、对象是否曾被删除过。
请求图片
PHImageManager.default().requestImage(for: <#T##PHAsset#>, targetSize: <#T##CGSize#>, contentMode: <#T##PHImageContentMode#>, options: <#T##PHImageRequestOptions?#>, resultHandler: <#T##(UIImage?, [AnyHashable : Any]?) -> Void#>)
targetSize
: 返回图片的尺寸
contentMode
:决定了照片应该以按比例缩放还是按比例填充的方式放到目标大小内。
options
: PHImageRequestOptions类用于定制请求。上面的方法返回指定尺寸的图像,如果你仅仅指定必要的参数而没有对 options 进行配置的话,返回的图像尺寸将会是原始图像的尺寸。或者,你指定的尺寸很小,这时候会按照你的要求来返回接近该尺寸的图像。
PHImageRequestOptions
synchronous
: 指定请求是否同步执行。
resizeMode
: 对请求的图像怎样缩放。有三种选择:None,不缩放;Fast,尽快地提供接近或稍微大于要求的尺寸;Exact,精准提供要求的尺寸。
deliveryMode
: 图像质量。有三种值:Opportunistic,在速度与质量中均衡;HighQualityFormat,不管花费多长时间,提供高质量图像;FastFormat,以最快速度提供好的质量。
normalizedCropRect
: 用于对原始尺寸的图像进行裁剪,基于比例坐标。只在 resizeMode 为 Exact 时有效。
resizeMode
默认是 None,这也造成了返回图像尺寸与要求尺寸不符。这点需要注意。要返回一个指定尺寸的图像需要避免两层陷阱:一定要指定 options 参数,resizeMode 不能为 None。
是否裁剪图片由 targetSize、contentMode来控制,裁剪的尺寸由targetSize、contentMode、resizeMode指定。