一、 PhotoKit的變量:
PHAdjustmentData?:
When a user edits an asset, photos saces a PHAdjustmentData object along with the modified image or video data;
在用戶編輯一個Asset時, 相冊會存儲該Asset在修改image或者video數據的過程
PHFetchOptions:
獲取資源時的參數, 可以傳nil, 即使用系統默認值。
PHFetchResult:
表示一系列的資源結果集合,也可以是相冊的集合, 從PHCollection的類方法中獲得。
PHImageManager:
用于處理資源的加載, 加載圖片的過程帶有緩存處理,可以通過傳入一個PHImageRequestOptions控制資源的輸出尺寸等規格。
PHImageRequestOptions:
控制加載圖片時的一系列參數。
PHAsset:
A PHAsset object represent an image or video file that appears in the Photos app, including iCloud Photos content.
一個PHAsset對象代表相冊中或者云儲存中的一個image或者Video文件;
PHAssetChangeRequest:
you create and use PHAssetChangeRequest objects within a photo library change block to create, delete ,or modify PHAsset objects.
當你在相冊中增刪改PHAsset對象時需要使用PHAssetChangeRequest對象。
PHAssetCreationRequest:
a PHAssetCreationRequest object , used within a photo library change block, constructs a new photo or video asset from data resources, ?and adds it to the photos library;
PHAssetCreationRequest對象用于在照片庫的增刪改查操作,創建一個新的image和video Asset從Data resources中, 然后將其加入Photos Library中;
PHAssetCollectionChangeRequest:
you create and use PHAssetCollectionChangeRequest objects within a photo library change block to create, delete, or modigy PHAssetCollection objects;
當你對photo Library 即 assetCollection進行增刪改操作時, 使用PHAssetCollectionChangeRequest對象
PHAssetResourceCreationOptions:
you use a PHAssetResourceCreationOptions abject to specify options when creating a new asset from data resources with a PHAssetCreationRequest Object.
通過PHAssetResourceCreationOptions對象來指定當創建新的Asset的options;
PHAssetResourceManager:
PHAssetResourceManager 對象提供方法訪問關聯相機Asset資源的基礎數據存儲
PHAssetCollection:
PHCollection的子類, 表示一個相冊或者一個時刻, 或者一個智能相冊(系統提供的特定相冊)。
PHPhotoLibrary:(重點)
公共的PHPhotoLibrary對象代表用戶的相冊庫,所有的圖片和相冊管理都要PHPhotoLibrary管理;
PHCollectionList:
表示一組PHCollection, 它本身也是一個PHCollection, 因此PHCollection作為一個集合,可以包含其他集合,這使到PhotoKit的組成比ALAssetsLibrary要復雜一些,
二、PhotoKit的機制
1)、獲取資源
PhotoKit 是采用“獲取”的方式拉取資源, 這些獲取的手段,都是一系列形如class func fetchXXX(..., options: PHFetchOptions) -> PHFetchResult的類方法。具體使用哪個類方法, 則視乎需要獲取的是相冊、時刻還是西苑, 這類方法中的Option充當了過濾器的作用。可以過濾相冊的類型、日期、名稱等。從而直接獲取對應的資源二不需要枚舉;
例如:
// 列出所有相冊智能相冊
PHFetchResult *smartAlbums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum subtype:PHAssetCollectionSubtypeAlbumRegular options:nil];
// 列出所有用戶創建的相冊
PHFetchResult *topLevelUserCollections = [PHCollectionList fetchTopLevelUserCollectionsWithOptions:nil];
// 獲取所有資源的集合,并按資源的創建時間排序
PHFetchOptions *options = [[PHFetchOptions alloc] init];
options.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:YES]];
PHFetchResult *assetsFetchResults = [PHAsset fetchAssetsWithOptions:options];
如前面提到過的那樣,從PHAssetCollection獲取中獲取到的可以是相冊也可以是資源, 但無論是那種內容, 都統一使用PHFetchResult對象封裝起來, 因此雖然PHAssetCollection回去到的結果可以是多樣的, 但通過PHFetchResult就可以使用統一的方法去處理這些內容(即遍歷PHFetchResult);
例子:
// 列出所有相冊智能相冊
PHFetchResult *smartAlbums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum subtype:PHAssetCollectionSubtypeAlbumRegular options:nil];
// 這時 smartAlbums 中保存的應該是各個智能相冊對應的 PHAssetCollection
for (NSInteger i = 0; i < fetchResult.count; i++) {
// 獲取一個相冊(PHAssetCollection)
PHCollection *collection = fetchResult[i];
if ([collection isKindOfClass:[PHAssetCollection class]]) {
PHAssetCollection *assetCollection = (PHAssetCollection *)collection;
// 從每一個智能相冊中獲取到的 PHFetchResult 中包含的才是真正的資源(PHAsset)
PHFetchResult *fetchResult = [PHAsset fetchAssetsInAssetCollection:assetCollection options:fetchOptions];
else {
NSAssert(NO, @"Fetch collection not PHCollection: %@", collection);
}
}
// 獲取所有資源的集合,并按資源的創建時間排序
PHFetchOptions *options = [[PHFetchOptions alloc] init];
options.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:YES]];
PHFetchResult *assetsFetchResults = [PHAsset fetchAssetsWithOptions:options];
// 這時 assetsFetchResults 中包含的,應該就是各個資源(PHAsset)
for (NSInteger i = 0; i < fetchResult.count; i++) {
// 獲取一個資源(PHAsset)
PHAsset *asset = fetchResult[i];
}
2)、獲取圖像的方式與坑點:
PhotoKit無法直接從PHAsset的實體中獲取圖像,而引入了一個管理器PHImageManager獲取圖像,PHImageManager是通過請求的方式拉取圖像,并可以控制請求得到的圖像的尺寸、剪切方式、質量、緩存及請求本身的管理(發出請求、取消請求)等,而請求圖像的方法是PHImageManager的一個實例方法:
- (PHImageRequestID)requestImageForAsset:(PHAsset *)asset
targetSize:(CGSize)targetSize
contentMode:(PHImageContentMode)contentMode
options:(nullable PHImageRequestOptions *)options
resultHandler:(void (^)(UIImage *__nullable result, NSDictionary *__nullable info))resultHandler;
這個方法中的參數坑點不少,下面逐個參數舉例一下其作用及坑點:
Asset: 圖像對應的PHAsset。
targetSize: 需要獲取的圖像的尺寸, 如果輸入的尺寸大于資源原圖的尺寸, 則需要返回原圖。
注意:在PHImageManager中, 所有的尺寸都是用Pixel作為單位(note that all sizes are in pixels),因此這里想要獲取正確大大小圖像, 需要把輸入的尺寸轉為Pixel。如果需要返回原圖尺寸,可以傳入PhotoKit中預先定義到的常量PHImageManagerMaximumSize, 表示返回可選范圍的最大尺寸, 即原圖尺寸;
contentMode: 圖像的剪切方式, 與UIView的contentMode參數相似,控制照片應該按比例縮放還是按比例填充的方式放到最終展示容器內;
注意:如果targetSize傳入PHImageManagerMaximumSize, 則contentMode無論傳入什么指都會被視為PHImageContentModeDefault。
options: 一個PHImageRequestOptions的實例,可以控制的內容相當豐富, 包含圖形的質量、版本,也會有參數控制圖像的剪切,下面在展開說明;
resultHandler:請求結束后輩調用的Block, 返回喲呵包含資源對于圖像的UIImage和包含圖像信息的一個NSDictionary, 在整個請求的周期中, 這個block可能會被調用多次,關于這點連同options參數在下面展開說明;
3)、PHImageRequestOptions與iCloud照片庫:
PHImageRequestOptions中包含了一系列控制請求圖像的屬性。
①、?resizeMode屬性控制圖像的剪切,不知道為什么PhotoKit會在請求圖像方法(requestImageForAsset)中已經有控制圖像剪切的參數后(contentMode),還在options中加入控制剪切屬性, 但如果兩個地方所控制的剪切結果有所沖突, PhotoKit會以resizeMode的結果為準。另外,resizeMode也有控制圖形質量的作用,如resizeMode設置為PHImageRequestOptionsResizeModeExact則返回圖像必須和目標代銷匹配,并且圖形質量也為高質量圖像,而設置為PHImageRequestOptionsResizeModeFast則請求的效率更高,但返回的圖像可能和目標大小不一樣并且質量較低。
②、在PhotoKit中, 對iCloud照片庫有很好的支持,如果用戶開啟了iCloud照片庫, 并且選擇了“優化iPhone、iPad存儲空間”, 或者選擇了“下載并保留原件”但原件內有加載好的時候, PhotoKit也會預選拿到這些非本地圖像的PHAsset, 但是由于本地并沒有原圖,所以如果產生了請求高清圖的請求, PhotoKit會嘗試從iCloud現在圖片, 而這個行為最終的表現,會被PHImageRequestOptions中的值所影響。PHImageRequestOptions中常常會用到幾個屬性:
例如:
1、networkAccessAllowed:此參數控制時候允許網絡請求, 默認是NO。如果不允許網絡請求, 那么不用設置;當然也拉取不到iCloud的圖像原件;
2、deliveryMode: 則用于控制請求圖片質量;
3、synchronous: 控制是否為同步請求, 默認為NO,如果為YES, 即同步請求時,deliveryMode會被視為PHImageRequestOptionsDeliveryModeHighQualityFormat, 即自動返回高質量的圖片, 因此不建議使用同步請求,否則如果界面需要等待返回的圖像才能進一步做出反應,則反應時長會很長。
4、progressHandler: 當圖片需要從iCloud下載時, 這個block會被自動調用,block中會返回圖像下載的進度、圖像的信息、出錯信息。開發者可以利用這些信息反饋給用戶當前圖形的現在進度以及狀態。但需要注意progressHandler不在主線程上執行, 因此在其中需要操作UI, 則需要手動放到主線程執行;
5、versions: 這個屬性是指獲取的圖像是否包含系統相冊“編輯”功能處理過的信息(如濾鏡、旋轉等), 這點比ALAssetLibrary要靈活,
③、上面提到,requestImageForAsset中的參數resultHandler可能會被多次調用,這種情況就是圖片需要從iCloud中下載的情況,在requestImageForAsset中返回的內容中,一開始的那一次請求中會返回一個小尺寸的圖像版本,當高清圖像還在下載時, 開發者可以首先給用戶展示這個低清的圖像版本,然后block在多次調用后, 最終返回高清的原圖, 至于當前返回的圖像是哪兒個版本圖像,可以通過block返回的NSDictionary info中獲知,PHImageResultlsDegradedKey表示當前返回的UIImage是低清圖,如果判斷是否已經獲得高清圖,可以這樣判斷:
// 排除取消,錯誤,低清圖三種情況,即已經獲取到了高清圖
BOOL downloadFinined = ![[info objectForKey:PHImageCancelledKey] boolValue] && ![info objectForKey:PHImageErrorKey] && ![[info objectForKey:PHImageResultIsDegradedKey] boolValue];
注意:
當我們使用requestImageForAsset發出對圖像的請求時, 如果在同一個PHImageManager中同時對同一個資源發出圖像請求, 請求的進度是可以共享的,因此我們可以利用這個特性, 吧PHImageManager以單例的形式使用, 這樣在切換界面時也不用擔心無法傳遞原圖的下載進度, 例如:在圖像的列表頁面觸發了下載圖像, 當我們離開列表頁面進入預覽大圖界面時, 并不用擔心會重新下載圖像, 只要沒有手動取消圖像下載, 進入大圖預覽界面下載圖像會自動繼續從上次的僅需下載圖像。
如果需要取消下載圖像,則可以使用PHImageManager的cancelImageRequest方法, 它傳入的是請求圖像的請求ID, 這個ID可以從requestImageForAsset的返回值中獲得,當然也可以從前面提到的包含圖形信息的NSDictionary info中獲得,當然前提是這和接受取消的PHImageManager與剛剛發送請求的PHImageManager是同一個實例,如上面所述式樣單例是最為簡單有效的方式;
4)、獲取圖像的優化:
PHImageManager提供了一個子類PHImageCachingManager用于處理圖像的緩存,但是這個子類并不只是圖像本身的緩存, 而是更加實用—處理圖形的整個加載過程的緩存
- (void)startCachingImagesForAssets:(NSArray *)assets targetSize:(CGSize)targetSize contentMode:(PHImageContentMode)contentMode options:(nullable PHImageRequestOptions *)options;
總結更為簡易可行的緩存方法:
獲取圖像時盡量獲取預覽圖,不要直接顯示原件,建議獲取與設備屏統樣大小的圖像即可,實際系統相冊預覽大圖時使用的也是預覽圖,這也是系統相冊加載速度快的原因。
獲取圖片使用異步請求, 如上面所述, 當請求為異步時返回圖像的block會被多次調用,先返回低清圖,再返回高清圖, 這樣一來可以大大減少UI的等待時間。
獲取到高清圖后可以緩存下來, 簡單的使用變量緩存即可,盡量在獲取高清圖后避免再次發起請求獲取圖像, 因為即使圖像原件已經下載了,從新請求高清圖時因為圖片的尺寸比較大, 因此系統會生成圖形和剪切圖形也會話費一些時間;
預先加載圖像, 如像預覽大圖這類情景中, 用戶同時只會看到一張大圖,因此在觀看某一張圖片時,預先請求其鄰近兩張圖片, 對于加快UI響應很有幫助。
三、常用方法的封裝:
1)、原圖:
由于原圖的尺寸通常會比較大,因此建議使用異步拉取, 但這里仍同時列舉同步拉取方法,這里需要留意如前文總所述,ALAssetRepresentation中獲取原圖的接口fullResolutionImage所得到的圖像并沒有帶上系統相冊“編輯”(選擇, 濾鏡等)效果。 需要額外獲取這些效果并手工疊加到圖像上。
.h文件
/// Asset 的原圖(包含系統相冊“編輯”功能處理后的效果)
- (UIImage *)originImage;
/**
*? 異步請求 Asset 的原圖,包含了系統照片“編輯”功能處理后的效果(剪裁,旋轉和濾鏡等),可能會有網絡請求
*
*? @param completion??????? 完成請求后調用的 block,參數中包含了請求的原圖以及圖片信息,在 iOS 8.0 或以上版本中,
*?????????????????????????? 這個 block 會被多次調用,其中第一次調用獲取到的尺寸很小的低清圖,然后不斷調用,直接獲取到高清圖,
*?????????????????????????? 獲取到高清圖后 QMUIAsset 會緩存起這張高清圖,這時 block 中的第二個參數(圖片信息)返回的為 nil。
*? @param phProgressHandler 處理請求進度的 handler,不在主線程上執行,在 block 中修改 UI 時注意需要手工放到主線程處理。
*
*? @wraning iOS 8.0 以下中并沒有異步請求預覽圖的接口,因此實際上為同步請求,這時 block 中的第二個參數(圖片信息)返回的為 nil。
*
*? @return 返回請求圖片的請求 id
*/
- (NSInteger)requestOriginImageWithCompletion:(void (^)(UIImage *, NSDictionary *))completion withProgressHandler:(PHAssetImageProgressHandler)phProgressHandler;
.m文件
- (UIImage *)originImage {
if (_originImage) {
return _originImage;
}
__block UIImage *resultImage;
if (_usePhotoKit) {
PHImageRequestOptions *phImageRequestOptions = [[PHImageRequestOptions alloc] init];
phImageRequestOptions.synchronous = YES;
[[[QMUIAssetsManager sharedInstance] phCachingImageManager] requestImageForAsset:_phAsset
targetSize:PHImageManagerMaximumSize
contentMode:PHImageContentModeDefault
options:phImageRequestOptions
resultHandler:^(UIImage *result, NSDictionary *info) {
resultImage = result;
}];
} else {
CGImageRef fullResolutionImageRef = [_alAssetRepresentation fullResolutionImage];
// 通過 fullResolutionImage 獲取到的的高清圖實際上并不帶上在照片應用中使用“編輯”處理的效果,需要額外在 AlAssetRepresentation 中獲取這些信息
NSString *adjustment = [[_alAssetRepresentation metadata] objectForKey:@"AdjustmentXMP"];
if (adjustment) {
// 如果有在照片應用中使用“編輯”效果,則需要獲取這些編輯后的濾鏡,手工疊加到原圖中
NSData *xmpData = [adjustment dataUsingEncoding:NSUTF8StringEncoding];
CIImage *tempImage = [CIImage imageWithCGImage:fullResolutionImageRef];
NSError *error;
NSArray *filterArray = [CIFilter filterArrayFromSerializedXMP:xmpData
inputImageExtent:tempImage.extent
error:&error];
CIContext *context = [CIContext contextWithOptions:nil];
if (filterArray && !error) {
for (CIFilter *filter in filterArray) {
[filter setValue:tempImage forKey:kCIInputImageKey];
tempImage = [filter outputImage];
}
fullResolutionImageRef = [context createCGImage:tempImage fromRect:[tempImage extent]];
}
}
// 生成最終返回的 UIImage,同時把圖片的 orientation 也補充上去
resultImage = [UIImage imageWithCGImage:fullResolutionImageRef scale:[_alAssetRepresentation scale] orientation:(UIImageOrientation)[_alAssetRepresentation orientation]];
}
_originImage = resultImage;
return resultImage;
}
- (NSInteger)requestOriginImageWithCompletion:(void (^)(UIImage *, NSDictionary *))completion withProgressHandler:(PHAssetImageProgressHandler)phProgressHandler {
if (_usePhotoKit) {
if (_originImage) {
// 如果已經有緩存的圖片則直接拿緩存的圖片
if (completion) {
completion(_originImage, nil);
}
return 0;
} else {
PHImageRequestOptions *imageRequestOptions = [[PHImageRequestOptions alloc] init];
imageRequestOptions.networkAccessAllowed = YES; // 允許訪問網絡
imageRequestOptions.progressHandler = phProgressHandler;
return [[[QMUIAssetsManager sharedInstance] phCachingImageManager] requestImageForAsset:_phAsset targetSize:PHImageManagerMaximumSize contentMode:PHImageContentModeDefault options:imageRequestOptions resultHandler:^(UIImage *result, NSDictionary *info) {
// 排除取消,錯誤,低清圖三種情況,即已經獲取到了高清圖時,把這張高清圖緩存到 _originImage 中
BOOL downloadFinined = ![[info objectForKey:PHImageCancelledKey] boolValue] && ![info objectForKey:PHImageErrorKey] && ![[info objectForKey:PHImageResultIsDegradedKey] boolValue];
if (downloadFinined) {
_originImage = result;
}
if (completion) {
completion(result, info);
}
}];
}
} else {
if (completion) {
completion([self originImage], nil);
}
return 0;
}
}
2)、縮略圖
相對于在拉取原圖時 ALAssetLibrary 的部分需要手工疊加系統相冊的“編輯”效果,拉取縮略圖則簡單一些,因為系統接口拉取到的縮略圖已經帶上“編輯”的效果了。
.h文件
/**
*? Asset 的縮略圖
*
*? @param size 指定返回的縮略圖的大小,僅在 iOS 8.0 及以上的版本有效,其他版本則調用 ALAsset 的接口由系統返回一個合適當前平臺的圖片
*
*? @return Asset 的縮略圖
*/
- (UIImage *)thumbnailWithSize:(CGSize)size;
/**
*? 異步請求 Asset 的縮略圖,不會產生網絡請求
*
*? @param size?????? 指定返回的縮略圖的大小,僅在 iOS 8.0 及以上的版本有效,其他版本則調用 ALAsset 的接口由系統返回一個合適當前平臺的圖片
*? @param completion 完成請求后調用的 block,參數中包含了請求的縮略圖以及圖片信息,在 iOS 8.0 或以上版本中,這個 block 會被多次調用,
*??????????????????? 其中第一次調用獲取到的尺寸很小的低清圖,然后不斷調用,直接獲取到高清圖,獲取到高清圖后 QMUIAsset 會緩存起這張高清圖,
*??????????????????? 這時 block 中的第二個參數(圖片信息)返回的為 nil。
*
*? @return 返回請求圖片的請求 id
*/
- (NSInteger)requestThumbnailImageWithSize:(CGSize)size completion:(void (^)(UIImage *, NSDictionary *))completion;
.m 文件
- (UIImage *)thumbnailWithSize:(CGSize)size {
if (_thumbnailImage) {
return _thumbnailImage;
}
__block UIImage *resultImage;
if (_usePhotoKit) {
PHImageRequestOptions *phImageRequestOptions = [[PHImageRequestOptions alloc] init];
phImageRequestOptions.resizeMode = PHImageRequestOptionsResizeModeExact;
// 在 PHImageManager 中,targetSize 等 size 都是使用 px 作為單位,因此需要對targetSize 中對傳入的 Size 進行處理,寬高各自乘以 ScreenScale,從而得到正確的圖片
[[[QMUIAssetsManager sharedInstance] phCachingImageManager] requestImageForAsset:_phAsset
targetSize:CGSizeMake(size.width * ScreenScale, size.height * ScreenScale)
contentMode:PHImageContentModeAspectFill options:phImageRequestOptions
resultHandler:^(UIImage *result, NSDictionary *info) {
resultImage = result;
}];
} else {
CGImageRef thumbnailImageRef = [_alAsset thumbnail];
if (thumbnailImageRef) {
resultImage = [UIImage imageWithCGImage:thumbnailImageRef];
}
}
_thumbnailImage = resultImage;
return resultImage;
}
- (NSInteger)requestThumbnailImageWithSize:(CGSize)size completion:(void (^)(UIImage *, NSDictionary *))completion {
if (_usePhotoKit) {
if (_thumbnailImage) {
if (completion) {
completion(_thumbnailImage, nil);
}
return 0;
} else {
PHImageRequestOptions *imageRequestOptions = [[PHImageRequestOptions alloc] init];
imageRequestOptions.resizeMode = PHImageRequestOptionsResizeModeExact;
// 在 PHImageManager 中,targetSize 等 size 都是使用 px 作為單位,因此需要對targetSize 中對傳入的 Size 進行處理,寬高各自乘以 ScreenScale,從而得到正確的圖片
return [[[QMUIAssetsManager sharedInstance] phCachingImageManager] requestImageForAsset:_phAsset targetSize:CGSizeMake(size.width * ScreenScale, size.height * ScreenScale) contentMode:PHImageContentModeAspectFill options:imageRequestOptions resultHandler:^(UIImage *result, NSDictionary *info) {
// 排除取消,錯誤,低清圖三種情況,即已經獲取到了高清圖時,把這張高清圖緩存到 _thumbnailImage 中
BOOL downloadFinined = ![[info objectForKey:PHImageCancelledKey] boolValue] && ![info objectForKey:PHImageErrorKey] && ![[info objectForKey:PHImageResultIsDegradedKey] boolValue];
if (downloadFinined) {
_thumbnailImage = result;
}
if (completion) {
completion(result, info);
}
}];
}
} else {
if (completion) {
completion([self thumbnailWithSize:size], nil);
}
return 0;
}
}
3)、 預覽圖
與上面的方法類似,不再展開說明。
.h 文件
/**
*? Asset 的預覽圖
*
*? @warning 仿照 ALAssetsLibrary 的做法輸出與當前設備屏幕大小相同尺寸的圖片,如果圖片原圖小于當前設備屏幕的尺寸,則只輸出原圖大小的圖片
*? @return Asset 的全屏圖
*/
- (UIImage *)previewImage;
/**
*? 異步請求 Asset 的預覽圖,可能會有網絡請求
*
*? @param completion??????? 完成請求后調用的 block,參數中包含了請求的預覽圖以及圖片信息,在 iOS 8.0 或以上版本中,
*?????????????????????????? 這個 block 會被多次調用,其中第一次調用獲取到的尺寸很小的低清圖,然后不斷調用,直接獲取到高清圖,
*?????????????????????????? 獲取到高清圖后 QMUIAsset 會緩存起這張高清圖,這時 block 中的第二個參數(圖片信息)返回的為 nil。
*? @param phProgressHandler 處理請求進度的 handler,不在主線程上執行,在 block 中修改 UI 時注意需要手工放到主線程處理。
*
*? @wraning iOS 8.0 以下中并沒有異步請求預覽圖的接口,因此實際上為同步請求,這時 block 中的第二個參數(圖片信息)返回的為 nil。
*
*? @return 返回請求圖片的請求 id
*/
- (NSInteger)requestPreviewImageWithCompletion:(void (^)(UIImage *, NSDictionary *))completion withProgressHandler:(PHAssetImageProgressHandler)phProgressHandler;
.m 文件
- (UIImage *)previewImage {
if (_previewImage) {
return _previewImage;
}
__block UIImage *resultImage;
if (_usePhotoKit) {
PHImageRequestOptions *imageRequestOptions = [[PHImageRequestOptions alloc] init];
imageRequestOptions.synchronous = YES;
[[[QMUIAssetsManager sharedInstance] phCachingImageManager] requestImageForAsset:_phAsset
targetSize:CGSizeMake(SCREEN_WIDTH, SCREEN_HEIGHT)
contentMode:PHImageContentModeAspectFill
options:imageRequestOptions
resultHandler:^(UIImage *result, NSDictionary *info) {
resultImage = result;
}];
} else {
CGImageRef fullScreenImageRef = [_alAssetRepresentation fullScreenImage];
resultImage = [UIImage imageWithCGImage:fullScreenImageRef];
}
_previewImage = resultImage;
return resultImage;
}
- (NSInteger)requestPreviewImageWithCompletion:(void (^)(UIImage *, NSDictionary *))completion withProgressHandler:(PHAssetImageProgressHandler)phProgressHandler {
if (_usePhotoKit) {
if (_previewImage) {
// 如果已經有緩存的圖片則直接拿緩存的圖片
if (completion) {
completion(_previewImage, nil);
}
return 0;
} else {
PHImageRequestOptions *imageRequestOptions = [[PHImageRequestOptions alloc] init];
imageRequestOptions.networkAccessAllowed = YES; // 允許訪問網絡
imageRequestOptions.progressHandler = phProgressHandler;
return [[[QMUIAssetsManager sharedInstance] phCachingImageManager] requestImageForAsset:_phAsset targetSize:CGSizeMake(SCREEN_WIDTH, SCREEN_HEIGHT) contentMode:PHImageContentModeAspectFill options:imageRequestOptions resultHandler:^(UIImage *result, NSDictionary *info) {
// 排除取消,錯誤,低清圖三種情況,即已經獲取到了高清圖時,把這張高清圖緩存到 _previewImage 中
BOOL downloadFinined = ![[info objectForKey:PHImageCancelledKey] boolValue] && ![info objectForKey:PHImageErrorKey] && ![[info objectForKey:PHImageResultIsDegradedKey] boolValue];
if (downloadFinined) {
_previewImage = result;
}
if (completion) {
completion(result, info);
}
}];
}
} else {
if (completion) {
completion([self previewImage], nil);
}
return 0;
}
}
4)、方向(imageOrientation)
比較奇怪的是,無論在 PhotoKit 或者是 ALAssetLibrary 中,要想獲取到準確的圖像方向,只能通過某些 key 檢索所得。
.h 文件
- (UIImageOrientation)imageOrientation;
.m文件
- (UIImageOrientation)imageOrientation {
UIImageOrientation orientation;
if (_usePhotoKit) {
if (!_phAssetInfo) {
// PHAsset 的 UIImageOrientation 需要調用過 requestImageDataForAsset 才能獲取
[self requestPhAssetInfo];
}
// 從 PhAssetInfo 中獲取 UIImageOrientation 對應的字段
orientation = (UIImageOrientation)[_phAssetInfo[@"orientation"] integerValue];
} else {
orientation = (UIImageOrientation)[[_alAsset valueForProperty:@"ALAssetPropertyOrientation"] integerValue];
}
return orientation;
}