相冊 - PhotoKit

一、 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數(shù)據(jù)的過程

PHFetchOptions:

獲取資源時的參數(shù), 可以傳nil, 即使用系統(tǒng)默認(rèn)值。

PHFetchResult:

表示一系列的資源結(jié)果集合,也可以是相冊的集合, 從PHCollection的類方法中獲得。

PHImageManager:

用于處理資源的加載, 加載圖片的過程帶有緩存處理,可以通過傳入一個PHImageRequestOptions控制資源的輸出尺寸等規(guī)格。

PHImageRequestOptions:

控制加載圖片時的一系列參數(shù)。

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.

當(dāng)你在相冊中增刪改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對象用于在照片庫的增刪改查操作,創(chuàng)建一個新的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;

當(dāng)你對photo Library 即 assetCollection進(jìn)行增刪改操作時, 使用PHAssetCollectionChangeRequest對象

PHAssetResourceCreationOptions:

you use a PHAssetResourceCreationOptions abject to specify options when creating a new asset from data resources with a PHAssetCreationRequest Object.

通過PHAssetResourceCreationOptions對象來指定當(dāng)創(chuàng)建新的Asset的options;

PHAssetResourceManager:

PHAssetResourceManager 對象提供方法訪問關(guān)聯(lián)相機(jī)Asset資源的基礎(chǔ)數(shù)據(jù)存儲

PHAssetCollection:

PHCollection的子類, 表示一個相冊或者一個時刻, 或者一個智能相冊(系統(tǒng)提供的特定相冊)。

PHPhotoLibrary:(重點)

公共的PHPhotoLibrary對象代表用戶的相冊庫,所有的圖片和相冊管理都要PHPhotoLibrary管理;

PHCollectionList:

表示一組PHCollection, 它本身也是一個PHCollection, 因此PHCollection作為一個集合,可以包含其他集合,這使到PhotoKit的組成比ALAssetsLibrary要復(fù)雜一些,

二、PhotoKit的機(jī)制

1)、獲取資源

PhotoKit 是采用“獲取”的方式拉取資源, 這些獲取的手段,都是一系列形如class func fetchXXX(..., options: PHFetchOptions) -> PHFetchResult的類方法。具體使用哪個類方法, 則視乎需要獲取的是相冊、時刻還是西苑, 這類方法中的Option充當(dāng)了過濾器的作用。可以過濾相冊的類型、日期、名稱等。從而直接獲取對應(yīng)的資源二不需要枚舉;

例如:

// 列出所有相冊智能相冊

PHFetchResult *smartAlbums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum subtype:PHAssetCollectionSubtypeAlbumRegular options:nil];

// 列出所有用戶創(chuàng)建的相冊

PHFetchResult *topLevelUserCollections = [PHCollectionList fetchTopLevelUserCollectionsWithOptions:nil];

// 獲取所有資源的集合,并按資源的創(chuàng)建時間排序

PHFetchOptions *options = [[PHFetchOptions alloc] init];

options.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:YES]];

PHFetchResult *assetsFetchResults = [PHAsset fetchAssetsWithOptions:options];

如前面提到過的那樣,從PHAssetCollection獲取中獲取到的可以是相冊也可以是資源, 但無論是那種內(nèi)容, 都統(tǒng)一使用PHFetchResult對象封裝起來, 因此雖然PHAssetCollection回去到的結(jié)果可以是多樣的, 但通過PHFetchResult就可以使用統(tǒng)一的方法去處理這些內(nèi)容(即遍歷PHFetchResult);

例子:

// 列出所有相冊智能相冊

PHFetchResult *smartAlbums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum subtype:PHAssetCollectionSubtypeAlbumRegular options:nil];

// 這時 smartAlbums 中保存的應(yīng)該是各個智能相冊對應(yīng)的 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);

}

}

// 獲取所有資源的集合,并按資源的創(chuàng)建時間排序

PHFetchOptions *options = [[PHFetchOptions alloc] init];

options.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:YES]];

PHFetchResult *assetsFetchResults = [PHAsset fetchAssetsWithOptions:options];

// 這時 assetsFetchResults 中包含的,應(yīng)該就是各個資源(PHAsset)

for (NSInteger i = 0; i < fetchResult.count; i++) {

// 獲取一個資源(PHAsset)

PHAsset *asset = fetchResult[i];

}

2)、獲取圖像的方式與坑點:

PhotoKit無法直接從PHAsset的實體中獲取圖像,而引入了一個管理器PHImageManager獲取圖像,PHImageManager是通過請求的方式拉取圖像,并可以控制請求得到的圖像的尺寸、剪切方式、質(zhì)量、緩存及請求本身的管理(發(fā)出請求、取消請求)等,而請求圖像的方法是PHImageManager的一個實例方法:

- (PHImageRequestID)requestImageForAsset:(PHAsset *)asset

targetSize:(CGSize)targetSize

contentMode:(PHImageContentMode)contentMode

options:(nullable PHImageRequestOptions *)options

resultHandler:(void (^)(UIImage *__nullable result, NSDictionary *__nullable info))resultHandler;

這個方法中的參數(shù)坑點不少,下面逐個參數(shù)舉例一下其作用及坑點:

Asset: 圖像對應(yīng)的PHAsset。

targetSize: 需要獲取的圖像的尺寸, 如果輸入的尺寸大于資源原圖的尺寸, 則需要返回原圖。

注意:在PHImageManager中, 所有的尺寸都是用Pixel作為單位(note that all sizes are in pixels),因此這里想要獲取正確大大小圖像, 需要把輸入的尺寸轉(zhuǎn)為Pixel。如果需要返回原圖尺寸,可以傳入PhotoKit中預(yù)先定義到的常量PHImageManagerMaximumSize, 表示返回可選范圍的最大尺寸, 即原圖尺寸;

contentMode: 圖像的剪切方式, 與UIView的contentMode參數(shù)相似,控制照片應(yīng)該按比例縮放還是按比例填充的方式放到最終展示容器內(nèi);

注意:如果targetSize傳入PHImageManagerMaximumSize, 則contentMode無論傳入什么指都會被視為PHImageContentModeDefault。

options: 一個PHImageRequestOptions的實例,可以控制的內(nèi)容相當(dāng)豐富, 包含圖形的質(zhì)量、版本,也會有參數(shù)控制圖像的剪切,下面在展開說明;

resultHandler:請求結(jié)束后輩調(diào)用的Block, 返回喲呵包含資源對于圖像的UIImage和包含圖像信息的一個NSDictionary, 在整個請求的周期中, 這個block可能會被調(diào)用多次,關(guān)于這點連同options參數(shù)在下面展開說明;

3)、PHImageRequestOptions與iCloud照片庫:

PHImageRequestOptions中包含了一系列控制請求圖像的屬性。

①、?resizeMode屬性控制圖像的剪切,不知道為什么PhotoKit會在請求圖像方法(requestImageForAsset)中已經(jīng)有控制圖像剪切的參數(shù)后(contentMode),還在options中加入控制剪切屬性, 但如果兩個地方所控制的剪切結(jié)果有所沖突, PhotoKit會以resizeMode的結(jié)果為準(zhǔn)。另外,resizeMode也有控制圖形質(zhì)量的作用,如resizeMode設(shè)置為PHImageRequestOptionsResizeModeExact則返回圖像必須和目標(biāo)代銷匹配,并且圖形質(zhì)量也為高質(zhì)量圖像,而設(shè)置為PHImageRequestOptionsResizeModeFast則請求的效率更高,但返回的圖像可能和目標(biāo)大小不一樣并且質(zhì)量較低。

②、在PhotoKit中, 對iCloud照片庫有很好的支持,如果用戶開啟了iCloud照片庫, 并且選擇了“優(yōu)化iPhone、iPad存儲空間”, 或者選擇了“下載并保留原件”但原件內(nèi)有加載好的時候, PhotoKit也會預(yù)選拿到這些非本地圖像的PHAsset, 但是由于本地并沒有原圖,所以如果產(chǎn)生了請求高清圖的請求, PhotoKit會嘗試從iCloud現(xiàn)在圖片, 而這個行為最終的表現(xiàn),會被PHImageRequestOptions中的值所影響。PHImageRequestOptions中常常會用到幾個屬性:

例如:

1、networkAccessAllowed:此參數(shù)控制時候允許網(wǎng)絡(luò)請求, 默認(rèn)是NO。如果不允許網(wǎng)絡(luò)請求, 那么不用設(shè)置;當(dāng)然也拉取不到iCloud的圖像原件;

2、deliveryMode: 則用于控制請求圖片質(zhì)量;

3、synchronous: 控制是否為同步請求, 默認(rèn)為NO,如果為YES, 即同步請求時,deliveryMode會被視為PHImageRequestOptionsDeliveryModeHighQualityFormat, 即自動返回高質(zhì)量的圖片, 因此不建議使用同步請求,否則如果界面需要等待返回的圖像才能進(jìn)一步做出反應(yīng),則反應(yīng)時長會很長。

4、progressHandler: 當(dāng)圖片需要從iCloud下載時, 這個block會被自動調(diào)用,block中會返回圖像下載的進(jìn)度、圖像的信息、出錯信息。開發(fā)者可以利用這些信息反饋給用戶當(dāng)前圖形的現(xiàn)在進(jìn)度以及狀態(tài)。但需要注意progressHandler不在主線程上執(zhí)行, 因此在其中需要操作UI, 則需要手動放到主線程執(zhí)行;

5、versions: 這個屬性是指獲取的圖像是否包含系統(tǒng)相冊“編輯”功能處理過的信息(如濾鏡、旋轉(zhuǎn)等), 這點比ALAssetLibrary要靈活,

③、上面提到,requestImageForAsset中的參數(shù)resultHandler可能會被多次調(diào)用,這種情況就是圖片需要從iCloud中下載的情況,在requestImageForAsset中返回的內(nèi)容中,一開始的那一次請求中會返回一個小尺寸的圖像版本,當(dāng)高清圖像還在下載時, 開發(fā)者可以首先給用戶展示這個低清的圖像版本,然后block在多次調(diào)用后, 最終返回高清的原圖, 至于當(dāng)前返回的圖像是哪兒個版本圖像,可以通過block返回的NSDictionary info中獲知,PHImageResultlsDegradedKey表示當(dāng)前返回的UIImage是低清圖,如果判斷是否已經(jīng)獲得高清圖,可以這樣判斷:

// 排除取消,錯誤,低清圖三種情況,即已經(jīng)獲取到了高清圖

BOOL downloadFinined = ![[info objectForKey:PHImageCancelledKey] boolValue] && ![info objectForKey:PHImageErrorKey] && ![[info objectForKey:PHImageResultIsDegradedKey] boolValue];

注意:

當(dāng)我們使用requestImageForAsset發(fā)出對圖像的請求時, 如果在同一個PHImageManager中同時對同一個資源發(fā)出圖像請求, 請求的進(jìn)度是可以共享的,因此我們可以利用這個特性, 吧PHImageManager以單例的形式使用, 這樣在切換界面時也不用擔(dān)心無法傳遞原圖的下載進(jìn)度, 例如:在圖像的列表頁面觸發(fā)了下載圖像, 當(dāng)我們離開列表頁面進(jìn)入預(yù)覽大圖界面時, 并不用擔(dān)心會重新下載圖像, 只要沒有手動取消圖像下載, 進(jìn)入大圖預(yù)覽界面下載圖像會自動繼續(xù)從上次的僅需下載圖像。

如果需要取消下載圖像,則可以使用PHImageManager的cancelImageRequest方法, 它傳入的是請求圖像的請求ID, 這個ID可以從requestImageForAsset的返回值中獲得,當(dāng)然也可以從前面提到的包含圖形信息的NSDictionary info中獲得,當(dāng)然前提是這和接受取消的PHImageManager與剛剛發(fā)送請求的PHImageManager是同一個實例,如上面所述式樣單例是最為簡單有效的方式;

4)、獲取圖像的優(yōu)化:

PHImageManager提供了一個子類PHImageCachingManager用于處理圖像的緩存,但是這個子類并不只是圖像本身的緩存, 而是更加實用—處理圖形的整個加載過程的緩存

- (void)startCachingImagesForAssets:(NSArray *)assets targetSize:(CGSize)targetSize contentMode:(PHImageContentMode)contentMode options:(nullable PHImageRequestOptions *)options;

總結(jié)更為簡易可行的緩存方法:

獲取圖像時盡量獲取預(yù)覽圖,不要直接顯示原件,建議獲取與設(shè)備屏統(tǒng)樣大小的圖像即可,實際系統(tǒng)相冊預(yù)覽大圖時使用的也是預(yù)覽圖,這也是系統(tǒng)相冊加載速度快的原因。

獲取圖片使用異步請求, 如上面所述, 當(dāng)請求為異步時返回圖像的block會被多次調(diào)用,先返回低清圖,再返回高清圖, 這樣一來可以大大減少UI的等待時間。

獲取到高清圖后可以緩存下來, 簡單的使用變量緩存即可,盡量在獲取高清圖后避免再次發(fā)起請求獲取圖像, 因為即使圖像原件已經(jīng)下載了,從新請求高清圖時因為圖片的尺寸比較大, 因此系統(tǒng)會生成圖形和剪切圖形也會話費一些時間;

預(yù)先加載圖像, 如像預(yù)覽大圖這類情景中, 用戶同時只會看到一張大圖,因此在觀看某一張圖片時,預(yù)先請求其鄰近兩張圖片, 對于加快UI響應(yīng)很有幫助。

三、常用方法的封裝:

1)、原圖:

由于原圖的尺寸通常會比較大,因此建議使用異步拉取, 但這里仍同時列舉同步拉取方法,這里需要留意如前文總所述,ALAssetRepresentation中獲取原圖的接口fullResolutionImage所得到的圖像并沒有帶上系統(tǒng)相冊“編輯”(選擇, 濾鏡等)效果。 需要額外獲取這些效果并手工疊加到圖像上。

.h文件

/// Asset 的原圖(包含系統(tǒng)相冊“編輯”功能處理后的效果)

- (UIImage *)originImage;

/**

*? 異步請求 Asset 的原圖,包含了系統(tǒng)照片“編輯”功能處理后的效果(剪裁,旋轉(zhuǎn)和濾鏡等),可能會有網(wǎng)絡(luò)請求

*

*? @param completion??????? 完成請求后調(diào)用的 block,參數(shù)中包含了請求的原圖以及圖片信息,在 iOS 8.0 或以上版本中,

*?????????????????????????? 這個 block 會被多次調(diào)用,其中第一次調(diào)用獲取到的尺寸很小的低清圖,然后不斷調(diào)用,直接獲取到高清圖,

*?????????????????????????? 獲取到高清圖后 QMUIAsset 會緩存起這張高清圖,這時 block 中的第二個參數(shù)(圖片信息)返回的為 nil。

*? @param phProgressHandler 處理請求進(jìn)度的 handler,不在主線程上執(zhí)行,在 block 中修改 UI 時注意需要手工放到主線程處理。

*

*? @wraning iOS 8.0 以下中并沒有異步請求預(yù)覽圖的接口,因此實際上為同步請求,這時 block 中的第二個參數(shù)(圖片信息)返回的為 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 獲取到的的高清圖實際上并不帶上在照片應(yīng)用中使用“編輯”處理的效果,需要額外在 AlAssetRepresentation 中獲取這些信息

NSString *adjustment = [[_alAssetRepresentation metadata] objectForKey:@"AdjustmentXMP"];

if (adjustment) {

// 如果有在照片應(yīng)用中使用“編輯”效果,則需要獲取這些編輯后的濾鏡,手工疊加到原圖中

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 也補(bǔ)充上去

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) {

// 如果已經(jīng)有緩存的圖片則直接拿緩存的圖片

if (completion) {

completion(_originImage, nil);

}

return 0;

} else {

PHImageRequestOptions *imageRequestOptions = [[PHImageRequestOptions alloc] init];

imageRequestOptions.networkAccessAllowed = YES; // 允許訪問網(wǎng)絡(luò)

imageRequestOptions.progressHandler = phProgressHandler;

return [[[QMUIAssetsManager sharedInstance] phCachingImageManager] requestImageForAsset:_phAsset targetSize:PHImageManagerMaximumSize contentMode:PHImageContentModeDefault options:imageRequestOptions resultHandler:^(UIImage *result, NSDictionary *info) {

// 排除取消,錯誤,低清圖三種情況,即已經(jīng)獲取到了高清圖時,把這張高清圖緩存到 _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 的部分需要手工疊加系統(tǒng)相冊的“編輯”效果,拉取縮略圖則簡單一些,因為系統(tǒng)接口拉取到的縮略圖已經(jīng)帶上“編輯”的效果了。

.h文件

/**

*? Asset 的縮略圖

*

*? @param size 指定返回的縮略圖的大小,僅在 iOS 8.0 及以上的版本有效,其他版本則調(diào)用 ALAsset 的接口由系統(tǒng)返回一個合適當(dāng)前平臺的圖片

*

*? @return Asset 的縮略圖

*/

- (UIImage *)thumbnailWithSize:(CGSize)size;

/**

*? 異步請求 Asset 的縮略圖,不會產(chǎn)生網(wǎng)絡(luò)請求

*

*? @param size?????? 指定返回的縮略圖的大小,僅在 iOS 8.0 及以上的版本有效,其他版本則調(diào)用 ALAsset 的接口由系統(tǒng)返回一個合適當(dāng)前平臺的圖片

*? @param completion 完成請求后調(diào)用的 block,參數(shù)中包含了請求的縮略圖以及圖片信息,在 iOS 8.0 或以上版本中,這個 block 會被多次調(diào)用,

*??????????????????? 其中第一次調(diào)用獲取到的尺寸很小的低清圖,然后不斷調(diào)用,直接獲取到高清圖,獲取到高清圖后 QMUIAsset 會緩存起這張高清圖,

*??????????????????? 這時 block 中的第二個參數(shù)(圖片信息)返回的為 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 進(jìn)行處理,寬高各自乘以 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 進(jìn)行處理,寬高各自乘以 ScreenScale,從而得到正確的圖片

return [[[QMUIAssetsManager sharedInstance] phCachingImageManager] requestImageForAsset:_phAsset targetSize:CGSizeMake(size.width * ScreenScale, size.height * ScreenScale) contentMode:PHImageContentModeAspectFill options:imageRequestOptions resultHandler:^(UIImage *result, NSDictionary *info) {

// 排除取消,錯誤,低清圖三種情況,即已經(jīng)獲取到了高清圖時,把這張高清圖緩存到 _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)、 預(yù)覽圖

與上面的方法類似,不再展開說明。

.h 文件

/**

*? Asset 的預(yù)覽圖

*

*? @warning 仿照 ALAssetsLibrary 的做法輸出與當(dāng)前設(shè)備屏幕大小相同尺寸的圖片,如果圖片原圖小于當(dāng)前設(shè)備屏幕的尺寸,則只輸出原圖大小的圖片

*? @return Asset 的全屏圖

*/

- (UIImage *)previewImage;

/**

*? 異步請求 Asset 的預(yù)覽圖,可能會有網(wǎng)絡(luò)請求

*

*? @param completion??????? 完成請求后調(diào)用的 block,參數(shù)中包含了請求的預(yù)覽圖以及圖片信息,在 iOS 8.0 或以上版本中,

*?????????????????????????? 這個 block 會被多次調(diào)用,其中第一次調(diào)用獲取到的尺寸很小的低清圖,然后不斷調(diào)用,直接獲取到高清圖,

*?????????????????????????? 獲取到高清圖后 QMUIAsset 會緩存起這張高清圖,這時 block 中的第二個參數(shù)(圖片信息)返回的為 nil。

*? @param phProgressHandler 處理請求進(jìn)度的 handler,不在主線程上執(zhí)行,在 block 中修改 UI 時注意需要手工放到主線程處理。

*

*? @wraning iOS 8.0 以下中并沒有異步請求預(yù)覽圖的接口,因此實際上為同步請求,這時 block 中的第二個參數(shù)(圖片信息)返回的為 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) {

// 如果已經(jīng)有緩存的圖片則直接拿緩存的圖片

if (completion) {

completion(_previewImage, nil);

}

return 0;

} else {

PHImageRequestOptions *imageRequestOptions = [[PHImageRequestOptions alloc] init];

imageRequestOptions.networkAccessAllowed = YES; // 允許訪問網(wǎng)絡(luò)

imageRequestOptions.progressHandler = phProgressHandler;

return [[[QMUIAssetsManager sharedInstance] phCachingImageManager] requestImageForAsset:_phAsset targetSize:CGSizeMake(SCREEN_WIDTH, SCREEN_HEIGHT) contentMode:PHImageContentModeAspectFill options:imageRequestOptions resultHandler:^(UIImage *result, NSDictionary *info) {

// 排除取消,錯誤,低清圖三種情況,即已經(jīng)獲取到了高清圖時,把這張高清圖緩存到 _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 中,要想獲取到準(zhǔn)確的圖像方向,只能通過某些 key 檢索所得。

.h 文件

- (UIImageOrientation)imageOrientation;

.m文件

- (UIImageOrientation)imageOrientation {

UIImageOrientation orientation;

if (_usePhotoKit) {

if (!_phAssetInfo) {

// PHAsset 的 UIImageOrientation 需要調(diào)用過 requestImageDataForAsset 才能獲取

[self requestPhAssetInfo];

}

// 從 PhAssetInfo 中獲取 UIImageOrientation 對應(yīng)的字段

orientation = (UIImageOrientation)[_phAssetInfo[@"orientation"] integerValue];

} else {

orientation = (UIImageOrientation)[[_alAsset valueForProperty:@"ALAssetPropertyOrientation"] integerValue];

}

return orientation;

}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容