因為項目的需求,要捕獲相冊中的視頻與圖片,之前也對之尚未做過什么探討,僅知道iOS8.0后,蘋果推出了新的相冊管理的包,那么正好,現(xiàn)在可以看看這個框架了,也知道AlAssetLibrary,使用的話倒是沒有詳細應(yīng)用過,因此也不與PhotoKit進行對比了。
類的介紹:
//PHCachingImageManager(PHImageManager的抽象) 處理圖像的整個加載過程的緩存要加載大量資源的縮略圖時可以使用該類的startCachingImage...預(yù)先將圖像加載到內(nèi)存中 ,使用時注意size要一致
先挑明自己踩到的坑..
1、 首次調(diào)用相冊權(quán)限的為難:僅會獲取相應(yīng)的權(quán)限提示 而不會響應(yīng)方法
//每次訪問相冊都會調(diào)用這個handler 檢查改app的授權(quán)情況
//PHPhotoLibrary
[PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
if (status == PHAuthorizationStatusAuthorized) {
//code
}
}];
2、獲取所有圖片(注意不能在膠卷中獲取圖片,因為膠卷中的圖片包含了video的顯示圖)
[PHAsset fetchAssetsWithMediaType:PHAssetMediaTypeImage options:nil];//這樣獲取
也可以使用PHFetchOptions中的謂詞過濾獲取
PHFetchOptions *fetchResoultOption = [[PHFetchOptions alloc] init];
fetchResoultOption.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:false]];
fetchResoultOption.predicate = [NSPredicate predicateWithFormat:@"mediaType = %d",PHAssetMediaTypeImage];
3、使用PHImageManager請求時的回調(diào)同步or異步時、block回調(diào)次數(shù)的問題
4、回調(diào)得出的圖片size的問題: 由3個參數(shù)決定
在ShowAlbumViewController 中觀察
在PHImageContentModeAspectFill 下 圖片size 有一個分水嶺 {125,125} {126,126}
當imageOptions.resizeMode = PHImageRequestOptionsResizeModeExact;
時: 設(shè)置size 小于{125,125}時,你得到的圖片size 將會是設(shè)置的1/2
而在PHImageContentModeAspectFit 分水嶺 {120,120} {121,121}
5、回調(diào)中info字典key消失的問題:
簡單地說,就是你如果是自定義的size,且返回的圖片的size小于源圖片的size 那么你將會得不到圖片的相對路徑等部分key
6、顯示的相冊名字不是中文:在info.plist 中設(shè)置
Localized resources can be mixed YES
本地資源的適配
7、This app has crashed because it attempted to access privacy-sensitive data
without a usage description.
The app's Info.plist must contain an NSPhotoLibraryUsageDescription key
with a string value explaining to the user how the app uses this data.
本app因未得到使用說明就直接企圖訪問私人數(shù)據(jù)而導(dǎo)致崩潰。
app想要訪問這些私人數(shù)據(jù)就得在info.plist增加一個以NSPhotoLibraryUsageDescription為鍵,以字符串為值的鍵值對。=
一、先來一個簡單的練習(xí)吧:獲取本機系統(tǒng)所有的圖片
在collectionView中
- (void)getAllPhotosFromAlbum {//配置簡單 ,但是參數(shù)卻是比價多且
self.options= [[PHImageRequestOptions alloc] init];//請求選項設(shè)置
self.options.resizeMode=PHImageRequestOptionsResizeModeExact;
//resizeMode 自定義設(shè)置圖片的大小 枚舉類型*
// PHImageRequestOptionsResizeMode:*
//PHImageRequestOptionsResizeModeNone = 0, //保持原size
//PHImageRequestOptionsResizeModeFast, //高效、但不保證圖片的size為自定義size
//PHImageRequestOptionsResizeModeExact, //嚴格按照自定義size
self.options.synchronous=YES; //YES 一定是同步 NO不一定是異步
imageOptions.resizeMode = PHImageRequestOptionsResizeModeExact;
/*
PHImageRequestOptionsResizeModeNone // 不調(diào)整大小
PHImageRequestOptionsResizeModeFast // 由系統(tǒng)去安排,情況不定:有時你設(shè)置的size比較低,會根據(jù)你設(shè)的size,有時又會比
PHImageRequestOptionsResizeModeExact// 保證精確到自定義size :此處精確的前提得用PHImageContentModeAspectFill
*/
//simageOptions.version = PHImageRequestOptionsVersionCurrent;//版本 iOS8.0之后出的圖片編輯extension,可以根據(jù)次枚舉獲取原圖或者是經(jīng)編輯過的圖片,
/*PHImageRequestOptionsVersion:
PHImageRequestOptionsVersionCurrent = 0, //當前的(編輯過?經(jīng)過編輯的圖:原圖)
PHImageRequestOptionsVersionUnadjusted, //經(jīng)過編輯的圖
PHImageRequestOptionsVersionOriginal //原始圖片
*/
// imageOptions.networkAccessAllowed = YES;//用于開啟iClould中下載圖片
// imageOptions.progressHandler //iClould下載進度的回調(diào)
imageOptions.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat;//在imageOptions.synchronous = NO的情況下最終決定是否是異步
//容器類
(PHFetchResult *) self.assets= [PHAsset fetchAssetsWithMediaType:PHAssetMediaTypeImage options:nil];
//此處option是對獲取得到對 Collection 的配置 我只是把它設(shè)為nil了 可以這樣使用
/*
//例如按資源的創(chuàng)建時間進行一個排序
PHFetchOptions *options = [[PHFetchOptions alloc] init];
// NSSortDescriptor *descriptor = [NSSortDescriptor sortDescriptorWithKey:nil ascending:YES];
options.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:YES]];
//其中:key是PHAsset類的屬性 這是一個kvc
PHFetchResult *assetsFetchResults = [PHAsset fetchAssetsWithOptions:options];
*/
[self.containView.collectionView reloadData];
}
- (UICollectionViewCell*)collectionView:(UICollectionView*)collectionView cellForItemAtIndexPath:(NSIndexPath*)indexPath {
AlbumCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:ALBUMCELLID forIndexPath:indexPath];
//cell.backgroundColor= [UIColor redColor];
CGSize size =CGSizeMake(50,50);//自定義image size變化情況頗為復(fù)雜 下面由說到
//返回一個 PHImageRequestID,在異步請求時可以根據(jù)這個ID去取消請求,同步就沒辦法了..
[[PHImageManager defaultManager] requestImageForAsset:self.assets[indexPath.row] targetSize:size contentMode:PHImageContentModeDefault options:self.options resultHandler:^(UIImage*_Nullable result,NSDictionary*_Nullable info) {
/*
最終產(chǎn)生圖片的size是有 imageOptions.resizeMode(即PHImageRequestOptions) 以及 PHImageContentMode 決定的,當然也有我們設(shè)定的size
優(yōu)先級而言
PHImageRequestOptions > PHImageContentMode
*/
//這個handler 并非在主線程上執(zhí)行,所以如果有UI的更新操作就得手動添加到主線程中
// dispatch_async(dispatch_get_main_queue(), ^{ //update UI });
#pragma If -[PHImageRequestOptions isSynchronous] returns NO (or options is nil), resultHandler may be called 1 or more times. .........異步就這個回調(diào)會調(diào)用1或多次....
#pragma If -[PHImageRequestOptions isSynchronous] returns YES, resultHandler will be called exactly once同步就1次.
//一開始本來打算是利用數(shù)據(jù)吧所有的相片先存起來的.但是發(fā)現(xiàn)同步會卡UI 但是異步又會被回調(diào)多次,我的數(shù)組都變成double了...只好把任務(wù)方法方法cellforItem中
cell.photoImageView.contentMode = UIViewContentModeScaleAspectFit;
cell.photoImageView.image = result;
}];
return cell;
}
/*注意這個info字典 有時這個info甚至為null 慎用
里面的key是比較奇怪的
盡量不要用里面的key
因為這個key 會變動: 當我們最終獲取到的圖片的size的高/寬 沒有一個達到能原有的圖片size的高/寬時
部分key 會消失 如 PHImageFileSandboxExtensionTokenKey , PHImageFileURLKey
*/
/*
在PHImageContentModeAspectFill 下 圖片size 有一個分水嶺 {125,125} {126,126}
當imageOptions.resizeMode = PHImageRequestOptionsResizeModeExact;
時: 設(shè)置size 小于{125,125}時,你得到的圖片size 將會是設(shè)置的1/2
而在PHImageContentModeAspectFit 分水嶺 {120,120} {121,121}
至于為什么會這樣???- - 可能蘋果考慮性能吧
*/
注意到上面這個resultHandler,返回一個UIImage,以及一個NSDictionary,圖片就不多說了,關(guān)鍵是這個info實在是詭異...
這就引起了我的關(guān)注:size和key的關(guān)系
圍繞著size的大小這個key竟然是不一樣的............
我把圖也打印出來了// 條件: size = {300,300}
本來是猜測我們設(shè)定的這個size比原圖小的時候,會產(chǎn)生一張新的圖片,而這張圖片本地資源是不存在的,所以少了部分的key,
但是現(xiàn)在就尷尬了,看第二個image的原圖,分明也是比{300,300}小的,為什么也沒了部分key了呢,不太懂....
大家不要誤會,第一張圖片的原圖是肯定大于{300,300} 的,為什么顯示不是{300,300},因為我使用處理圖片的模式為PHImageContentModeAspectFit,所以顯示下來的并非我們{300,300}而是保留原圖的比例,大家可以動手去試一試
而且這個size其實是像素:
CGSizeMake(self.assets[indexPath.row].pixelWidth,self.assets[indexPath.row].pixelHeight);
或者設(shè)定一個肯定比原圖大的size就能獲取到原圖了....當然,后者是不靠譜的...
或許是這個size出的問題吧,會導(dǎo)致如果我要根據(jù)這個PHimageFileKey 獲取本地的圖片的時候就因為某部分的image因為size的原因缺少這個key使得找不到這張圖,使得我app出現(xiàn)bug....不過這個方法真的挺不安全的,建議是把返回的result 那張image 寫進tmp里面 再使用吧。。。。
再者就是異步請求次數(shù)跟size的關(guān)系
當我的self.options.synchronous=NO;//就是一個異步請求的過程(后來發(fā)現(xiàn)其實異步請求并不由options的synchronous屬性決定)
而這個PHImageRequestOptions 可以根據(jù)它的一個屬性deliveryMode 去控制異步請求的次數(shù)
PHImageRequestOptionsDeliveryMode:
PHImageRequestOptionsDeliveryModeOpportunistic//根據(jù)我self.options.synchronous判斷返回結(jié)果是一個抑或多個
PHImageRequestOptionsDeliveryModeHighQualityFormat //制定的同步返回一個結(jié)果,返回的圖片質(zhì)量是比我們設(shè)定的size會好一點(實際上與PHImageRequestOptions的resizeMode枚舉相關(guān))
PHImageRequestOptionsDeliveryModeFastFormat//僅返回一次,效率較高之余獲得的圖質(zhì)量不太好
我這邊配合的resizeMode模式為強制自定義的圖片大小,即:
self.options.resizeMode=PHImageRequestOptionsResizeModeExact;//嚴格按照自定義圖片大小的加載模式
如果不是這個枚舉的話,返回的PHImageRequestOptionsDeliveryModeOpportunistic第一次返回的縮略圖,以及PHImageRequestOptionsDeliveryModeFastFormat所返回的縮略圖最小默認是{60,60},
PHImageRequestOptionsResizeModeExact則可以隨意設(shè)置
值得注意的是,當我們選擇的是PHImageRequestOptionsDeliveryModeOpportunistic時,返回的次數(shù)是跟size有關(guān)系的,你設(shè)定的的size 太小的話僅會回調(diào)一次,大的話,首次返回的是一個小的縮略圖(這個縮略圖最大是{60,60},具體值根據(jù)比例),二次回調(diào)才會得到我們想要的圖片
總而言之,圖片的真實size跟resizeMode和PHImageRequestOptionsDeliveryMode以及我們設(shè)定的size都有關(guān)聯(lián)吧,具體應(yīng)用的時候就要多試試幾次,觀察自己真實所需吧...
接下來的是我要找的是視頻......問題又出現(xiàn)了,這個PhotoKit有提供查找視頻的方式么...
然后我把這修改了一下。。。改成了找Video 的元數(shù)據(jù) 哈哈哈哈哈哈哈
self.assets= [PHAsset fetchAssetsWithMediaType:**PHAssetMediaTypeVideo** options:nil];
但我好像又踩上了自己挖的坑了...
全是圖片....不,不是這樣的。。。
在回頭看看發(fā)現(xiàn)有PHImageManager 這個單例提供我們3個方法去找得到Vide 我則選取了其中一個通用的方法
PHFetchResult *assetsResult = [PHAssetfetch AssetsWithMediaType:PHAssetMediaType Videooptions:nil];
PHVideoRequestOptions *options2 = [[PHVideoRequestOptions alloc] init];
options2.deliveryMode=PHVideoRequestOptionsDeliveryModeAutomatic;
for(PHAsset *a in assetsResult) {
[[PHImageManager defaultManager] requestAVAssetForVideo:a options:options2 resultHandler:^(AVAsset*_Nullable asset,
AVAudioMix*_Nullable audioMix,NSDictionary*_Nullable info) {
NSLog(@"%@",info);
}];
}
找到你了....
不過你居然在。。。PHImageFileSandboxExtensionTokenKey。。。這個key里面
看來地址是要用要用截取的了,可以直接輸入到finder就能找到這個視頻,但是前面的字符串就得讓我們手動去取其子字符串只需要拿到video的Path就可以了
很不安全,我真的怕這個key突然又因為什么原因而miss掉了........
然后我看了看asset 的類型 發(fā)現(xiàn)它的類型為AVURLAsset,有點意思,看看里面發(fā)現(xiàn)了一個是有個URL屬性的!而且是asset的相對路徑, 好像可以做點什么...
//video路徑獲取
if (asset && [asset isKindOfClass:[AVURLAsset class]] && [NSString stringWithFormat:@"%@",((AVURLAsset *)asset).URL].length > 0) {
NSString *videoURLStr = [NSString stringWithFormat:@"%@",((AVURLAsset *)asset).URL];
videoPath = ((AVURLAsset*)asset).URL.path;
}
二、根據(jù)實際需求的部分獲取資源
怎么個實際法呢,有時候你對著一大堆圖片,然后找啊找,調(diào)啊調(diào),還真的不是什么好方法...為什么就不能在我分好的類別中選取呢!!!!
好的,就下來就說這種情況,大家在開頭都一定看到那幅,資源與資源集合的一個關(guān)系圖...
獲取所有用戶自定義的相冊:1\2\3分別是我創(chuàng)建的相冊,以下代碼能捕獲到,自己創(chuàng)建的的相冊的內(nèi)容
捕獲系統(tǒng)相冊中的圖片/video
解釋一下
PHFetchResult*smartAlbums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum subtype:PHAssetCollectionSubtypeAlbumRegular options:nil];
這個獲取資源集合的方法 需要填寫兩個枚舉的類型,大概翻譯了下,有錯誤的大家可以提一下,我會非常感激的
demo地址
參考文章:
1、照片框架:https://objccn.io/issue-21-4/#PhotoKit-Object-Model%5D(http://objccn.io/issue-21-4/#PhotoKit-Object-Model
2、iOS 開發(fā)之照片框架詳解:http://kayosite.com/ios-development-and-detail-of-photo-framework.html