最近有自定义相册需求,就学习一了下 PhotoKit 简单记录一下笔记。
PhotoKit 是 iOS 8 引入的相比 AssetsLibrary 更完整也更高效的库,对资源的处理跟 AssetsLibrary 也有很大的不同。可以从官方 Session Introducing the Photos Frameworks 开始了解 PhotoKit。
此文提到的点的具体的实现可以看我的开源库: BMAssetPicker
基本概念
PhotoKit 所有PhotoKit对象都继承PHObject抽象类。
- PHAsset: 代表照片库中的一个资源,跟 ALAsset 类似,通过 PHAsset 可以获取和保存资源
- PHFetchOptions: 获取资源时的参数,可以传 nil,即使用系统默认值
- PHAssetCollection: PHCollection 的子类,表示一个相册或者一个时刻,或者是一个「智能相册(系统提供的特定的一系列相册,例如:最近删除,视频列表,收藏等等)
- PHFetchResult: 表示一系列的资源结果集合,也可以是相册的集合,从 PHCollection 的类方法中获得
- PHImageManager: 用于处理资源的加载,加载图片的过程带有缓存处理,可以通过传入一个 PHImageRequestOptions 控制资源的输出尺寸等规格
- PHImageRequestOptions: 如上面所说,控制加载图片时的一系列参数
- PHCollectionList: 表示一组PHCollections。在照片应用可以看到它,照片 - 时刻 - 精选 - 年度。
获取资源
从 PhotoKit 直接获取的最小资源单位为 PHAsset,是一个只读枚举类型。包含照片尺寸,日期,类型等。从 PHAsset 获取图像或视频需要依赖 PHImageManager。
做一个图片选择的思路:获取权限 —> 列出所有相册 —> 列出相册中的图片
获取权限
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| func authorize(status: PHAuthorizationStatus = PHPhotoLibrary.authorizationStatus(), completion: (authorized: Bool) -> Void) { switch status { case .Authorized: completion(authorized: true) case .NotDetermined: PHPhotoLibrary.requestAuthorization({ (status) -> Void in dispatch_async(dispatch_get_main_queue(), { () -> Void in self.authorize(status, completion: completion) }) }) default: () dispatch_async(dispatch_get_main_queue(), { () -> Void in completion(authorized: false) }) } }
|
获取相册
获取权限后可以按照智能相册或者用户相册形式获取相册列表。也可以直接获取所有图片
1 2 3 4 5 6 7 8 9 10
| let smartAlbums = PHAssetCollection.fetchAssetCollectionsWithType(PHAssetCollectionType.SmartAlbum, subtype: PHAssetCollectionSubtype.AlbumRegular, options: nil)
let topLevelUserCollections = PHCollectionList.fetchTopLevelUserCollectionsWithOptions(nil)
let options = PHFetchOptions() options.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: true)] let assetsFetchResults = PHAsset.fetchAssetsWithOptions(nil)
|
相册类型有一下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| PHAssetCollectionSubtype : Int { case AlbumRegular case AlbumSyncedEvent case AlbumSyncedFaces case AlbumSyncedAlbum case AlbumImported
case AlbumMyPhotoStream case AlbumCloudShared
case SmartAlbumGeneric case SmartAlbumPanoramas case SmartAlbumVideos case SmartAlbumFavorites case SmartAlbumTimelapses case SmartAlbumAllHidden case SmartAlbumRecentlyAdded case SmartAlbumBursts case SmartAlbumSlomoVideos case SmartAlbumUserLibrary @available(iOS 9.0, *) case SmartAlbumSelfPortraits @available(iOS 9.0, *) case SmartAlbumScreenshots
case Any }
|
从相册里面获取资源列表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| let result = PHAssetCollection.fetchAssetCollectionsWithType(PHAssetCollectionType.SmartAlbum, subtype: PHAssetCollectionSubtype.AlbumRegular, options: nil)
for i in 0..<result.count {
if let collection = result[i] as? PHAssetCollection { let fetchOptions = PHFetchOptions() fetchOptions.sortDescriptors = [ NSSortDescriptor(key: "creationDate", ascending: false) ] fetchOptions.predicate = NSPredicate(format: "mediaType = %d", PHAssetMediaType.Image.rawValue)
let result = PHAsset.fetchAssetsInAssetCollection(collection, options: fetchOptions)
} else { "Fetch collection not PHCollection: \(result[i])" } }
|
从 PHAsset 读取具体的数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
|
func fetchImageData(url:String, callBack:((data:NSData?)->Void)) { if let asset = imageList[url] { let options = PHImageRequestOptions() options.synchronous = false options.deliveryMode = .HighQualityFormat options.networkAccessAllowed = true PHImageManager.defaultManager().requestImageForAsset(asset, targetSize: PHImageManagerMaximumSize, contentMode: .Default, options: options) { (result, info) -> Void in if let image = result, data = UIImageJPEGRepresentation(image, 1.0) { callBack(data:data) } else { callBack(data: nil) } } } else { callBack(data: nil) } }
func fetchVideoFile(url:String, callBack:((fileURL:String?)->Void)) { if let asset = imageList[url] { let imageManager = PHImageManager.defaultManager() let videoRequestOptions = PHVideoRequestOptions()
videoRequestOptions.deliveryMode = .Automatic videoRequestOptions.version = .Current videoRequestOptions.networkAccessAllowed = true
imageManager.requestAVAssetForVideo(asset, options: videoRequestOptions, resultHandler: { (avAsset, avAudioMix, info) -> Void in
if let nextURLAsset = avAsset as? AVURLAsset, filepath = nextURLAsset.URL.path { callBack(fileURL: filepath) } else { callBack(fileURL: nil) } }) } else { return callBack(fileURL: nil) } }
|
其中 deliveryMode 有以下选项。
1 2 3 4 5 6
| public enum PHImageRequestOptionsDeliveryMode : Int { case Opportunistic case HighQualityFormat case FastFormat }
|
参考