由于前段時間,需要完成一個跟相冊的需求,所以閱讀了一些與圖片有關的文章,和使用了一些相關的第三方庫。
在 iOS 設備中,照片和視頻是相當重要的一部分。在 iOS 8 出現之前,開發者只能使用 AssetsLibrary
框架來訪問設備的照片庫,這是一個有點跟不上 iOS 應用發展步伐以及代碼設計原則但確實強大的框架,考慮到 iOS7 仍占有不少的滲透率,因此 AssetsLibrary
也是本文重點介紹的部分。隨著 iOS 8 的到來,蘋果給我們提供了一個現代化的框架 —— PhotoKit
,它比 AssetsLibrary
表現更好,并且擁有讓應用和設備照片庫無縫工作的特性。
另外值得強調的是,在 iOS 中,照片庫并不只是照片的集合,同時也包含了視頻。在 AssetsLibrary
中兩者都有相同類型的對象去描述,只是類型不同而已。文中為了方便,大部分時候會使用「資源」代表 iOS 中的「照片和視頻」。
PhotoKit 對象模型
PhotoKit
定義了與系統的 Photos 應用內展現給用戶的模型對象相一致的實體圖表。這些照片實體都是輕量級的不可變對象。所有的 PhotoKit
對象都是繼承自 PHObject
抽象基類,其公共接口只提供了一個 localIdentifier
屬性。
PHAsset
表示用戶照片庫中一個單獨的資源,用以提供資源的元數據。
成組的資源叫做資源集合,用 PHAssetCollection
類表示。一個單獨的資源集合可以是照片庫中的一個相冊或者一個時刻,或者是一個特殊的“智能相冊”。這種智能相冊包括所有的視頻集合,最近添加的項目,用戶收藏,所有連拍照片等等。PHAssetCollection
是 PHCollection
的子類。
PHCollectionList
表示一組的 PHCollections
。因為它本身就是 PHCollection
,所以集合列表可以包含其他集合列表,它們允許復雜的集合繼承。實際上,我們可以在照片應用的時刻欄目中看到它:照片 --- 時刻 --- 精選 --- 年度,就是一個例子。
獲取 (Fetch) 照片實體
獲取 vs. 枚舉
那些熟悉 AssetsLibrary
框架的開發者可能會記得 AssetsLibrary
可以用一些特定屬性來找到需要的資源,其中一個必須枚舉用戶資源庫來獲得匹配的資源。不得不承認,這個 API 雖然提供了一些縮小搜索域的方法,但還是十分低效。
而與之形成鮮明對比,PhotoKit
實體的實例是通過獲取得到的。那些熟悉 Core Data
的人,會覺得和 PhotoKit
在概念和描述都比較接近。
AssetsLibrary
的組成比較符合照片庫本身的組成,照片庫中的完整照片庫對象、相冊、相片都能在 AssetsLibrary
中找到一一對應的組成,這使到 AssetsLibrary
的使用變得直觀而方便。
AssetsLibrary: 代表整個設備中的資源庫(照片庫),通過 AssetsLibrary
可以獲取和包括設備中的照片和視頻
- ALAssetsGroup: 映射照片庫中的一個相冊,通過
ALAssetsGroup
可以獲取某個相冊的信息,相冊下的資源,同時也可以對某個相冊添加資源。 - ALAsset: 映射照片庫中的一個照片或視頻,通過
ALAsset
可以獲取某個照片或視頻的詳細信息,或者保存照片和視頻。 - ALAssetRepresentation:
ALAssetRepresentation
是對ALAsset
的封裝(但不是其子類),可以更方便地獲取 ALAsset 中的資源信息,每個 ALAsset 都有至少有一個ALAssetRepresentation
對象,可以通過defaultRepresentation
獲取。而例如使用系統相機應用拍攝的 RAW + JPEG 照片,則會有兩個ALAssetRepresentation
,一個封裝了照片的 RAW 信息,另一個則封裝了照片的 JPEG 信息。
獲取請求
獲取操作是由上面描述的實體的類方法實現的。要使用哪個類/方法,取決于問題所在范圍和你展示與遍歷照片庫的方式。所有獲取方法的命名都是相似的:class func fetchXXX(..., options: PHFetchOptions) -> PHFetchResult 。options 參數給了我們一個對結果進行過濾和排序的途徑,這和 NSFetchRequest
的 predicate
與 sortDescriptors
參數類似。
獲取結果
你可能已經注意到了這些獲取操作不是異步的。它們返回了一個 PHFetchResult
對象,可以用類似 NSArray 的接口來訪問結果內的集合。它會按需動態加載內容并且緩存最近請求的內容。這個行為和設置了 batchSize
屬性的 NSFetchRequest
返回的結果數組相似。對于 PHFetchResult
來說,沒有辦法用參數來指定這個行為,但是官網文檔保證 “即使在處理大量的返回結果時,依然能夠有最好的表現”。
PHImageManager 照片加載
在處理用戶照片庫的過去幾年中,開發者創造了上百 (如果沒有上千) 的小技巧來提高照片加載和展示的效率。這些技巧處理請求的派發和取消,圖像大小的修改和裁剪,緩存等等。PhotoKit 提供了一個可以用更加便捷和現代的 API 做了所有這些操作的類:PHImageManager
。
- (PHImageRequestID)requestImageForAsset:(PHAsset *)asset targetSize:(CGSize)targetSize contentMode:(PHImageContentMode)contentMode options:(nullable PHImageRequestOptions *)options resultHandler:(void (^)(UIImage *__nullable result, NSDictionary *__nullable info))resultHandler;
- (PHImageRequestID)requestImageDataForAsset:(PHAsset *)asset options:(nullable PHImageRequestOptions *)options resultHandler:(void(^)(NSData *__nullable imageData, NSString *__nullable dataUTI, UIImageOrientation orientation, NSDictionary *__nullable info))resultHandler;
PHImageRequestOptions 控制加載圖片時的參數
提供了一些方式來確定圖像管理器該以怎樣的方式來重新設置圖像大小。
resizeMode
屬性可以設置為 .Exact
(返回圖像必須和目標大小相匹配),.Fast
(比 .Exact 效率更高,但返回圖像可能和目標大小不一樣) 或者 .None
。
還有個值得一提的是,normalizedCroppingMode
屬性讓我們確定圖像管理器應該如何裁剪圖像。注意:如果設置了 normalizedcroppingMode
的值,那么 resizeMode
需要設置為 .Exact
。
結果回調 (result handler)
結果回調是一個包含了一個 UIImage 變量和一個 info 字典作為參數的 block。根據參數和請求的選項,在請求的整個生命周期,它可以被圖像管理器多次調用。
info
字典提供了關于當前請求狀態的信息,比如:
圖像是否必須從 iCloud 請求 (如果你初始化時將 networkAccessAllowed 設置成 false,那么就必須重新請求圖像) —— PHImageResultIsInCloudKey
。
當前遞送的 UIImage 是否是最終結果的低質量格式。當高質量圖像正在下載時,這個可以讓你給用戶先展示一個預覽圖像 —— PHImageResultIsDegradedKey
。
請求 ID (可以便捷的取消請求),以及請求是否已經被取消 —— PHImageResultRequestIDKey
和 PHImageCancelledKey
。
如果沒有圖像提供給 result handler,字典內還會有一個錯誤信息 —— PHImageErrorKey
。
TZImagePickerController(1.5.0)使用方法
方法一:
TZImagePickerController *imagePickerVc = [[TZImagePickerController alloc] initWithSelectedAssets:_selectedAssets selectedPhotos:_selectedPhotos index:indexPath.row];
imagePickerVc.allowPickingOriginalPhoto = self.allowPickingOriginalPhotoSwitch.isOn;
imagePickerVc.isSelectOriginalPhoto = _isSelectOriginalPhoto;
[imagePickerVc setDidFinishPickingPhotosHandle:^(NSArray<UIImage *> *photos, NSArray *assets, BOOL isSelectOriginalPhoto) {
_selectedPhotos = [NSMutableArray arrayWithArray:photos];
_selectedAssets = [NSMutableArray arrayWithArray:assets];
_isSelectOriginalPhoto = isSelectOriginalPhoto;
_layout.itemCount = _selectedPhotos.count;
[_collectionView reloadData];
_collectionView.contentSize = CGSizeMake(0, ((_selectedPhotos.count + 2) / 3 ) * (_margin + _itemWH));
}];
方法二:
首先遵守<TZImagePickerControllerDelegate>
TZImagePickerController *imagePickerVc = [[TZImagePickerController alloc] initWithMaxImagesCount:maxSelectCount delegate:self];
imagePickerVc.allowPickingOriginalPhoto = self.allowPickingOriginalPhotoSwitch.isOn;
imagePickerVc.isSelectOriginalPhoto = _isSelectOriginalPhoto;
imagePickerVc.sortAscendingByModificationDate = NO;
[self.navigationController presentViewController:imagePickerVc animated:YES completion:nil];
- (void)imagePickerController:(TZImagePickerController *)picker didFinishPickingPhotos:(NSArray<UIImage *> *)photos sourceAssets:(NSArray *)assets isSelectOriginalPhoto:(BOOL)isSelectOriginalPhoto {
imagePickerVc.sortAscendingByModificationDate = NO;
imagePickerVc.photoWidth = 1024.0;
imagePickerVc.photoPreviewMaxWidth = 3072.0;
[self.navigationController presentViewController:imagePickerVc animated:YES completion:nil];
}
注意:
1.在使用時,如果你不獲取原圖的話,TZImagePickerController的代理方法或block回調里返回的圖片是質量非常差的圖片,壓縮程度非常高。因為它是直接把PHImageResultIsDegradedKey
里的圖片返回,從字面上我們就可以看出,這是蘋果返回的一個退化的圖片,我在前面也說了,這只是返回的一個預覽圖像,并且TZImagePickerController在創建PHImageRequestOptions
的時候,resizeMode
使用的是PHImageRequestOptionsResizeModeFast
;如果把resizeMode
修改成PHImageRequestOptionsResizeModeNone
,可以適當的提高。
2.在外部設置photoPreviewMaxWidth
超出500~800無效,因為TZImagePickerController在內部設置了范圍。
- (void)setPhotoPreviewMaxWidth:(CGFloat)photoPreviewMaxWidth {
_photoPreviewMaxWidth = photoPreviewMaxWidth;
if (photoPreviewMaxWidth > 800) {
_photoPreviewMaxWidth = 800;
} else if (photoPreviewMaxWidth < 500) {
_photoPreviewMaxWidth = 500;
}
[TZImageManager manager].photoPreviewMaxWidth = _photoPreviewMaxWidth;
}
所以,如果需要設置的話注意一下。
由于長時間沒有去跟進 好多小伙伴的問題不能解決,萬分抱歉,大家有問題 可以提問,可能正好碰到有解決的 幫忙解答了,/::D/::D