前言
iOS8之后,蘋果推出新的管理圖庫資源框架Photos,相比于之前的ALAssetsLibrary框架來說,新框架更強大,功能更多,使得我們處理相冊的選擇性更多.下面,簡單介紹一下,新框架的使用.
Photos的基本構(gòu)成的介紹
-
和圖庫交互: 通過一個共享的圖庫對象,在用戶授權(quán)的前提下可以獲取圖庫資源,修改資源或者資源集合,監(jiān)測圖庫資源的更新.
- PHPhotoLibrary: 一個可以用來獲取或者改變用戶的圖庫的共享對象.
-
獲取和檢查資源: 下面這些類代表著圖庫中的內(nèi)容: 資源和集合,這些對象的實例都是只讀的,只包含元數(shù)據(jù).通過這些資源和集合,你可以抓取到特定條件的對象集合.
- PHAsset: 代表著圖庫里的一個圖片,視頻,Live Photo等
- PHAssetCollection: photos資源的集合,例如一個時刻,用戶創(chuàng)建的相冊,或者是智能相冊
- PHCollectionList: PHAssetCollection的集合,例如一年的時刻集合,或者包含用戶創(chuàng)建的相冊的文件夾.
- PHObject: Photos模型對象的抽象類(資源和集合).
- PHFetchResult: Photos抓取方法返回的資源或集合的有序列表
- PHFetchOptions: 一些選項的集合,它影響過濾,排序和你抓取資源或集合時對返回結(jié)果的管理.
-
加載資源內(nèi)容:使用這些類來請求和資源相關(guān)聯(lián)的圖片,Live Photo內(nèi)容.Photos會自動下載或生成你指定的圖片,緩存以便快速重用.你也可以請求預(yù)加載批量的圖片以便處理加載大量圖片資源的情況.
PHImageManager: 提供方法來獲取或者生成與assets相關(guān)聯(lián)的預(yù)覽縮率圖或者全尺寸圖片.
PHCachingImageManager: 相對于PHImageManager,它會針對加載大量assets資源時做出優(yōu)化.
PHImageRequestOptions: 一些選項的集合,它能影響你從assets獲取靜態(tài)圖片資源
PHVideoRequestOptions: 一些選項的集合,它能影響你從assets獲取Live Photo資源
PHLivePhoto: Live Photo的一個可以播放的展現(xiàn)形式 - 一個包含拍照前后時刻聲音和動作的圖片
-
請求改變: 為了修改assets或者collections, 你創(chuàng)建請求對象來描述你的編輯并且明確地把它提交給圖庫.這個結(jié)構(gòu)使編輯變得簡單,安全,并且在多線程或者多個APP,APP擴展之間編輯同樣的assets更高效.
- PHAssetChangeRequest: 一個請求,用于創(chuàng)建,刪除,改變源數(shù)據(jù),或者編輯一個photo asset的內(nèi)容,在圖庫修改block中使用.
- PHAssetCollectionChangeRequest: 一個請求,用于創(chuàng)建,刪除,改變源數(shù)據(jù),或者編輯一個Photo collection的內(nèi)容,在圖庫修改block中使用.
- PHCollectionListChangeRequest: 一個請求,用于創(chuàng)建,刪除,改變源數(shù)據(jù),或者編輯一個Photo collection list的內(nèi)容,在圖庫修改block中使用.
- PHObjectPlaceholder: 一個只讀的代理,表示一個已經(jīng)被創(chuàng)建用于change request的asset或者集合對象
-
監(jiān)聽改變: 無論是別的app,別的設(shè)備,或者你APP中的代碼改變了asset或者集合中一系列asset的元數(shù)據(jù),photos都會告訴你的app.
- PHPhotoLibraryChangeObserver: 你可以實現(xiàn)這個協(xié)議,當(dāng)圖庫發(fā)生了變化時,你會被通知.
- PHChange: 描述了圖庫發(fā)生的改變
- PHObjectChangeDetails: 描述了一個asset或者集合對象發(fā)生的改變
- PHFetchResultChangeDetails: 描述了在一個獲取結(jié)果中列舉的一系列asset或者集合對象的改變.
-
操作asset資源: 一個或者多個asset resource對象代表著每個asset表面下儲存的數(shù)據(jù).使用這些對象來直接操作那些資源 - 例如, 備份或者保存asset
- PHAssetResource: 關(guān)聯(lián)著圖庫中一個圖片,視頻或者Live Photo的數(shù)據(jù)資源.
- PHAssetCreationRequest: 一個請求,基于表面下的數(shù)據(jù)資源,來創(chuàng)建一個新的asset,在圖庫修改block中使用.
- PHAssetResourceCreationOptions: 一個選項的集合,這些選項影響著從隱藏資源創(chuàng)建一個新的asset.
- PHAssetResourceManager:提供方法來獲取與asset相關(guān)聯(lián)的數(shù)據(jù)資源.
- PHAssetResourceRequestOptions: 一個選項的集合,這些選項影響著你通過asset resource manager獲取數(shù)據(jù)的傳輸.
Photos的使用
下面我們來看看Photos的使用,通過下面的代碼,我們可以了解怎么使用這個框架,以及使用過程中的一些注意事項.
-
使用photos來獲取圖庫中的相冊.
首先我們創(chuàng)建一個項目,然后這里我們遍歷獲取圖庫的相冊,并把相冊展示在界面.
效果如上圖,這里我們獲取相冊數(shù)據(jù),并展示它的名稱信息.我封裝了一個工具類用于執(zhí)行獲取相冊,圖片資源等操作.
主要的代碼如下
//獲取相冊
- (void)requestCollectionWithHandler:(completion)handler {
__block completion operation = handler;
if ([self checkAuthorization]) {//授權(quán)成功
__block NSMutableArray *arr1 = [NSMutableArray new];
PHFetchResult *result1 = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum subtype:PHAssetCollectionSubtypeSmartAlbumTimelapses options:nil];
[result1 enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if ([obj isKindOfClass:[PHAssetCollection class]]) {
[arr1 addObject:(PHAssetCollection *)obj];
}
}];
__block NSMutableArray *arr2 = [NSMutableArray new];
PHFetchResult *result2 = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum subtype:PHAssetCollectionSubtypeAny options:nil];
[result2 enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if ([obj isKindOfClass:[PHAssetCollection class]]) {
[arr2 addObject:(PHAssetCollection *)obj];
}
}];
[arr1 addObjectsFromArray:arr2];
if (operation) {
dispatch_async(dispatch_get_main_queue(), ^{
operation(YES, arr1);
});
}
}else {//授權(quán)失敗
[PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
if (status == PHAuthorizationStatusAuthorized) {
[self requestCollectionWithHandler:operation];
}
}];
}
}
注意:
IDE環(huán)境:Xcode 9.2, 模擬器是iOS11.2的 iPhone8 plus, 由于iOS10對用戶隱私更加看重,所以infoplist必須添加"Privacy - Photo Library Usage Description"這個key,并且描述你的app想要使用圖庫的意圖.而且我們在獲取圖庫資源之前應(yīng)該先判斷用戶授權(quán)的狀態(tài),如果用戶未授權(quán)是不能獲取到數(shù)據(jù)的.
讓我們看一下上面使用到的一些參數(shù),首先,你獲取相冊的時候,需要指定相冊的類型和子類型,關(guān)于相冊的類型,請看下面的標(biāo)注
typedef NS_ENUM(NSInteger, PHAssetCollectionType) {
PHAssetCollectionTypeAlbum = 1, //從 iTunes 同步來的相冊脂矫,以及用戶在Photos中自己創(chuàng)建的相冊
PHAssetCollectionTypeSmartAlbum = 2, //經(jīng)由相機得來的相冊
PHAssetCollectionTypeMoment = 3, //Photos 為我們自動生成的時間分組的相冊
} PHOTOS_ENUM_AVAILABLE_IOS_TVOS(8_0, 10_0);
typedef NS_ENUM(NSInteger, PHAssetCollectionSubtype) {
// PHAssetCollectionTypeAlbum regular subtypes
PHAssetCollectionSubtypeAlbumRegular = 2, //用戶在 Photos 中創(chuàng)建的相冊
PHAssetCollectionSubtypeAlbumSyncedEvent = 3, /使用 iTunes 從 Photos 照片庫或者 iPhoto 照片庫同步過來的事件蜈块。
PHAssetCollectionSubtypeAlbumSyncedFaces = 4, //使用 iTunes 從 Photos 照片庫或者 iPhoto 照片庫同步的人物相冊。
PHAssetCollectionSubtypeAlbumSyncedAlbum = 5, //做了 AlbumSyncedEvent 應(yīng)該做的事
PHAssetCollectionSubtypeAlbumImported = 6, //從相機或是外部存儲導(dǎo)入的相冊
// PHAssetCollectionTypeAlbum shared subtypes
PHAssetCollectionSubtypeAlbumMyPhotoStream = 100, //用戶的 iCloud 照片流
PHAssetCollectionSubtypeAlbumCloudShared = 101, //用戶使用 iCloud 共享的相冊
// PHAssetCollectionTypeSmartAlbum subtypes
PHAssetCollectionSubtypeSmartAlbumGeneric = 200, //文檔解釋為非特殊類型的相冊魄梯,主要包括從 iPhoto 同步過來的相冊
PHAssetCollectionSubtypeSmartAlbumPanoramas = 201, //相機拍攝的全景照片
PHAssetCollectionSubtypeSmartAlbumVideos = 202, //相機拍攝的視頻
PHAssetCollectionSubtypeSmartAlbumFavorites = 203, //收藏文件夾
PHAssetCollectionSubtypeSmartAlbumTimelapses = 204, //延時視頻文件夾隙姿,同時也會出現(xiàn)在視頻文件夾中
PHAssetCollectionSubtypeSmartAlbumAllHidden = 205, //包含隱藏照片或視頻的文件夾
PHAssetCollectionSubtypeSmartAlbumRecentlyAdded = 206, //相機近期拍攝的照片或視頻
PHAssetCollectionSubtypeSmartAlbumBursts = 207, //連拍模式拍攝的照片
PHAssetCollectionSubtypeSmartAlbumSlomoVideos = 208, //Slomo 是 slow motion 的縮寫奈梳,高速攝影慢動作解析左腔,在該模式下铃剔,iOS 設(shè)備以120幀拍攝循榆。
PHAssetCollectionSubtypeSmartAlbumUserLibrary = 209, //這個命名最神奇了析恢,就是相機相冊,所有相機拍攝的照片或視頻都會出現(xiàn)在該相冊中秧饮,而且使用其他應(yīng)用保存的照片也會出現(xiàn)在這里氮昧。
PHAssetCollectionSubtypeSmartAlbumSelfPortraits PHOTOS_AVAILABLE_IOS_TVOS(9_0, 10_0) = 210, //這個相冊包含了用戶使用前置攝像頭拍攝的照片和視頻
PHAssetCollectionSubtypeSmartAlbumScreenshots PHOTOS_AVAILABLE_IOS_TVOS(9_0, 10_0) = 211, //使用設(shè)備的截屏功能生成的照片
PHAssetCollectionSubtypeSmartAlbumDepthEffect PHOTOS_AVAILABLE_IOS_TVOS(10_2, 10_1) = 212, //在可兼容的設(shè)備上使用景深攝像模式拍的照片
PHAssetCollectionSubtypeSmartAlbumLivePhotos PHOTOS_AVAILABLE_IOS_TVOS(10_3, 10_2) = 213, //包含所有的Live Photo資源
PHAssetCollectionSubtypeSmartAlbumAnimated PHOTOS_AVAILABLE_IOS_TVOS(11_0, 11_0) = 214, //動圖
PHAssetCollectionSubtypeSmartAlbumLongExposures PHOTOS_AVAILABLE_IOS_TVOS(11_0, 11_0) = 215, //長曝光
// Used for fetching, if you don't care about the exact subtype
PHAssetCollectionSubtypeAny = NSIntegerMax ////包含所有類型
} PHOTOS_ENUM_AVAILABLE_IOS_TVOS(8_0, 10_0);
注意,獲取指定類型的相冊時浦楣,主類型和子類型要匹配袖肥,不要串臺。如果不匹配振劳,系統(tǒng)會按照 Any 子類型來處理椎组。對于 Moment 類型,子類型使用 Any历恐。
1.獲取用戶自己建立的相冊和文件夾(我稱之為邏輯相冊寸癌,非系統(tǒng)相冊和從 iTunes 同步來的相冊)有兩種方法:
[PHCollection fetchTopLevelUserCollectionsWithOptions:nil];
[PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum subtype:PHAssetCollectionSubtypeAlbumRegular options:nil];
在沒有提供PHOptions的情況下,返回的PHFetchResult結(jié)果是按相冊的建立時間排序的弱贼,最新的在前面蒸苇。
2.獲取相機相冊:
[PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum subtype:PHAssetCollectionSubtypeSmartAlbumUserLibrary options:nil];
- 請求圖像
使用PHImageManager這個類來獲取資源,你應(yīng)該使用它的單例對象,而不是重新初始化一個實例對象.
[[PHImageManager defaultManager] requestImageForAsset: targetSize: contentMode: options: resultHandler:];
這個方法默認(rèn)是異步執(zhí)行的,resultHandler可能會被多次調(diào)用,因為對于指定的尺寸,Photos可能會先提供低質(zhì)量的圖像以供臨時顯示,隨后加載完指定尺寸的圖像后再返回符合要求的圖片.如果指定尺寸的高質(zhì)量圖片有緩存,那么就直接返回緩存,這些都可以通過options參數(shù)來指定.
俗話說得好,實踐出真知,我們下面通過代碼和模擬器來看一看這個圖片請求過程.
在上面生成的相冊中,我點擊一個相冊,進入一個新的控制器,在這個控制器里,我要展示相冊里包含的PHAsset的縮率圖,
請求圖片的代碼如下:
- (void)requestImgaeWithSize:(CGSize)size andAsset:(PHAsset *)asset andCompletionHandler:(void (^)(UIImage *))handler {
__block void (^ operation)(UIImage *) = handler;
PHImageRequestOptions *options = [PHImageRequestOptions new];
options.synchronous = YES;
options.resizeMode = PHImageRequestOptionsResizeModeExact;
options.deliveryMode = PHImageRequestOptionsDeliveryModeFastFormat;
options.normalizedCropRect = CGRectMake(0.25, 0.25, 0.5, 0.5);
[[PHCachingImageManager defaultManager] requestImageForAsset:asset targetSize:size contentMode:PHImageContentModeAspectFill options:options resultHandler:^(UIImage * _Nullable result, NSDictionary * _Nullable info) {
dispatch_async(dispatch_get_main_queue(), ^{
if (operation) {
operation(result);
operation = nil;
}
});
}];
}
然后我點擊了Camera Roll這個相冊,進去之后展示了這個相冊里的圖片;如下圖:
我們接下來 做一個操作,點擊某個item進入到詳細的圖片展示界面.然后我們把圖片寫入到本地桌面上,來分析請求圖片傳入的參數(shù)有啥影響.
我們點擊海綿寶寶這張圖片進入具體的圖片展示界面,這里我們做了一個操作,把這個獲取到的圖片寫到桌面了,
[[PhotosTool sharedTool] requestImgaeWithSize:CGSizeMake(_asset.pixelWidth, _asset.pixelWidth) andCropRect:CGRectMake(0, 0, 1, 1) andAsset:_asset andCompletionHandler:^(UIImage *result) {
_poster.image = result;
//把圖片寫入本地桌面
NSData *data = UIImagePNGRepresentation(result);
[data writeToFile:@"/Users/rjkfb2/Desktop/test.png" atomically:YES];
}];
然后,我們來看一下這個寫入到桌面的圖片的尺寸大小
2048*2048,像素為單位,這里原圖就是這個尺寸.
下面我們要仔細說下一PHImageRequestOptions這個類的作用.首先我們看一下這個類的實例對象有哪些屬性
@property (nonatomic, assign) PHImageRequestOptionsVersion version; // version
@property (nonatomic, assign) PHImageRequestOptionsDeliveryMode deliveryMode; // delivery mode. Defaults to PHImageRequestOptionsDeliveryModeOpportunistic
@property (nonatomic, assign) PHImageRequestOptionsResizeMode resizeMode; // resize mode. Does not apply when size is PHImageManagerMaximumSize. Defaults to PHImageRequestOptionsResizeModeNone (or no resize)
@property (nonatomic, assign) CGRect normalizedCropRect; // specify crop rectangle in unit coordinates of the original image, such as a face. Defaults to CGRectZero (not applicable)
@property (nonatomic, assign, getter=isNetworkAccessAllowed) BOOL networkAccessAllowed; // if necessary will download the image from iCloud (client can monitor or cancel using progressHandler). Defaults to NO (see start/stopCachingImagesForAssets)
@property (nonatomic, assign, getter=isSynchronous) BOOL synchronous; // return only a single result, blocking until available (or failure). Defaults to NO
@property (nonatomic, copy, nullable) PHAssetImageProgressHandler progressHandler; // provide caller a way to be told how much progress has been made prior to delivering the data when it comes from iCloud. Defaults to nil, shall be set by caller
version: 設(shè)置版本
deliveryMode: 請求的圖像質(zhì)量.有三種選擇: 1.PHImageRequestOptionsDeliveryModeOpportunistic: 在速度和質(zhì)量中均衡,當(dāng)調(diào)用方式為異步的,客戶端可能會獲得一些圖片,(類似于先返回模糊圖片,然后再返回清晰的圖片), 如果調(diào)用時同步的,那么只會返回一個圖片; 2.PHImageRequestOptionsDeliveryModeHighQualityFormat:不管花費多長時間,提供高質(zhì)量圖像; 3.PHImageRequestOptionsDeliveryModeFastFormat: 以最快的速度返回一個圖片,這個圖片可能會降低質(zhì)量. 這個屬性只有在 synchronous 為 true 時有效吮旅。
resizeMode:對請求的圖像怎樣縮放溪烤。有三種選擇:None,不縮放;Fast檬嘀,盡快地提供接近或稍微大于要求的尺寸槽驶;Exact,精準(zhǔn)提供要求的尺寸鸳兽。
normalizedCropRect:用于對原始尺寸的圖像進行裁剪掂铐,基于比例坐標(biāo)揍异。只在 resizeMode 為 Exact 時有效全陨。
基于上面那個獲取圖片的例子,我們來驗證一下resizeMode 對于返回的圖片的影響,如果上面那個請求圖片的代碼,我們修改一下,把resizeMode,改為
- (void)requestImgaeWithSize:(CGSize)size andCropRect:(CGRect)rect andAsset:(PHAsset *)asset andCompletionHandler:(void (^)(UIImage *))handler {
__block void (^ operation)(UIImage *) = handler;
PHImageRequestOptions *options = [PHImageRequestOptions new];
options.synchronous = YES;
options.resizeMode = PHImageRequestOptionsResizeModeFast;
options.deliveryMode = PHImageRequestOptionsDeliveryModeFastFormat;
[[PHCachingImageManager defaultManager] requestImageForAsset:asset targetSize:size contentMode:PHImageContentModeAspectFit options:options resultHandler:^(UIImage * _Nullable result, NSDictionary * _Nullable info) {
dispatch_async(dispatch_get_main_queue(), ^{
if (operation) {
operation(result);
operation = nil;
}
});
}];
}
這里再看看返回的圖片大小
這時候就不是你指定的尺寸了.由此可以體會到resizeMode參數(shù)的作用,resizeMode為fast的時候會以最快速度返回一個大小和指定尺寸相近的圖片,但是可能大小不一樣.
接下來,我們來看看photos一個很實用的功能,就是通過指定normalizedCropRect這個參數(shù),來返回裁剪好的圖片,這個參數(shù)是基于比例坐標(biāo),類似于layer的錨點這個屬性,X,Y軸最大值都是1,
代碼如下:
- (void)requestImgaeWithSize:(CGSize)size andAsset:(PHAsset *)asset andCompletionHandler:(void (^)(UIImage *))handler {
__block void (^ operation)(UIImage *) = handler;
PHImageRequestOptions *options = [PHImageRequestOptions new];
options.synchronous = YES;
options.resizeMode = PHImageRequestOptionsResizeModeExact;
options.deliveryMode = PHImageRequestOptionsDeliveryModeFastFormat;
options.normalizedCropRect = CGRectMake(0, 0, 0.5, 0.5);
[[PHCachingImageManager defaultManager] requestImageForAsset:asset targetSize:size contentMode:PHImageContentModeAspectFit options:options resultHandler:^(UIImage * _Nullable result, NSDictionary * _Nullable info) {
if (operation) {
operation(result);
operation = nil;
}
}];
}
讓我們來看一下返回的圖片
這時候可以看到返回的圖片就是原圖片的左上角,然后像素是你給定的100* 100.
demo下載地址
好啦,以上就是PhotosKit的簡單使用,第二篇文章也是有關(guān)Photoskit的使用以及使用過程中遇到的一些問題.