工做緣由,須要處理接入一個視頻模塊,在視頻選擇的時候遇到了一個不太容易發現的bug,產生的緣由是因爲手機內存小,而用戶又打開了相冊同步iCloud,git
加載中的圖片github
在這時,若是本地可用內存太小,會致使
將本地相冊中的圖片或視頻刪除只留縮略圖,若是App調用的時候想要選取這種圖片就須要從iCloud雲中進行下載,
才能獲取原圖或原視頻。ide
下面po下解決方案:fetch
若是你以前處理過相冊問題,那麼對以下的代碼確定不陌生,就是很普通的兩個系統級別的請求回調,獲取對應的圖片,視頻。atom
// get Image [[PHImageManager defaultManager] requestImageDataForAsset:asset options:nil resultHandler:^(NSData * _Nullable imageData, NSString * _Nullable dataUTI, UIImageOrientation orientation, NSDictionary * _Nullable info) { }]; // get Video [[PHImageManager defaultManager] requestPlayerItemForVideo:asset options:nil resultHandler:^(AVPlayerItem * _Nullable playerItem, NSDictionary * _Nullable info) { if (completion) completion(playerItem,info); }];
可是每每以前沒有注意到第二個輸入 options
是用來幹嗎的,
其實解決方案就來自於這個 PHImageRequestOptions
,PHVideoRequestOptions
。spa
這這兩個 options
都有一個共同的參數就是.net
@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)
系統的解釋也很詳細,若是賦值 YES
,那麼容許從 iCloud
中獲取圖片和視頻,默認是 NO
。線程
雖然這個問題解決不是很難,可是每每容易被忽略,因此記錄一下。code
這裏很是感謝@半遲塵大大的TZImagePickerController的源碼,這個是一個很是靠譜的相冊選擇圖片視頻的庫,而且處於仍在維護中。感興趣的能夠連接過去看一看源碼,寫的很好。orm
博主博客@HarwordLiu
下面po一下完整的這個問題的解決代碼:
/// Get Video - (void)getVideoOutputPathWithAsset:(PHAsset *)asset completion:(void (^)(NSString *outputPath))completion { PHVideoRequestOptions* options = [[PHVideoRequestOptions alloc] init]; options.version = PHVideoRequestOptionsVersionOriginal; options.deliveryMode = PHVideoRequestOptionsDeliveryModeAutomatic; options.networkAccessAllowed = YES; [[PHImageManager defaultManager] requestAVAssetForVideo:asset options:options resultHandler:^(AVAsset* avasset, AVAudioMix* audioMix, NSDictionary* info){ // NSLog(@"Info:\n%@",info); AVURLAsset *videoAsset = (AVURLAsset*)avasset; // NSLog(@"AVAsset URL: %@",myAsset.URL); [self startExportVideoWithVideoAsset:videoAsset completion:completion]; }]; } /// Get Image - (PHImageRequestID)getPhotoWithAsset:(PHAsset *)asset photoWidth:(CGFloat)photoWidth completion:(void (^)(UIImage *, NSDictionary *, BOOL isDegraded))completion { PHAsset *phAsset = (PHAsset *)asset; CGFloat aspectRatio = phAsset.pixelWidth / (CGFloat)phAsset.pixelHeight; CGFloat pixelWidth = photoWidth * 2.0; CGFloat pixelHeight = pixelWidth / aspectRatio; CGSize imageSize = CGSizeMake(pixelWidth, pixelHeight); // 修復獲取圖片時出現的瞬間內存太高問題 PHImageRequestOptions *option = [[PHImageRequestOptions alloc] init]; option.resizeMode = PHImageRequestOptionsResizeModeFast; PHImageRequestID imageRequestID = [[PHImageManager defaultManager] requestImageForAsset:asset targetSize:imageSize contentMode:PHImageContentModeAspectFill options:option resultHandler:^(UIImage * _Nullable result, NSDictionary * _Nullable info) { BOOL downloadFinined = (![[info objectForKey:PHImageCancelledKey] boolValue] && ![info objectForKey:PHImageErrorKey]); if (downloadFinined && result) { if (completion) completion(result,info,[[info objectForKey:PHImageResultIsDegradedKey] boolValue]); } // Download image from iCloud / 從iCloud下載圖片 if ([info objectForKey:PHImageResultIsInCloudKey] && !result) { PHImageRequestOptions *option = [[PHImageRequestOptions alloc]init]; option.networkAccessAllowed = YES; option.resizeMode = PHImageRequestOptionsResizeModeFast; [[PHImageManager defaultManager] requestImageDataForAsset:asset options:option resultHandler:^(NSData * _Nullable imageData, NSString * _Nullable dataUTI, UIImageOrientation orientation, NSDictionary * _Nullable info) { UIImage *resultImage = [UIImage imageWithData:imageData scale:0.1]; if (completion) completion(resultImage,info,[[info objectForKey:PHImageResultIsDegradedKey] boolValue]); }]; } }]; return imageRequestID; }
https://stackoverflow.com/questions/31966571/check-given-phasset-is-icloud-asset
第一種方法
[cloudAlbums enumerateObjectsUsingBlock:^(PHAssetCollection *collection, NSUInteger idx, BOOL *stop) { PHFetchResult *result = [PHAsset fetchAssetsInAssetCollection:collection options:fetchOptions]; [result enumerateObjectsUsingBlock:^(PHAsset *asset, NSUInteger idx, BOOL *stop) { PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init]; options.resizeMode = PHImageRequestOptionsResizeModeFast; options.synchronous = YES;
__block BOOL isICloudAsset = NO; [[PHImageManager defaultManager] requestImageForAsset:asset targetSize:imageSize contentMode:PHImageContentModeAspectFit options:options resultHandler:^(UIImage *result, NSDictionary *info) { //根據請求會調中的參數重 NSDictionary *info 是否有cloudKey 來判斷是不是 iCloud if ([info objectForKey: PHImageResultIsInCloudKey].boolValue) { isICloudAsset = YES; } }]; }]; }];
第二種方法(實際上是同一種)
__block BOOL isPhotoInICloud = NO; PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init]; options.networkAccessAllowed = YES; options.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat; // 下載iCloud 圖片的進度回調 只要圖片是在icloud中 而後去請求圖片就會走這個回調 若是圖片沒有在iCloud中不回走這個回調 //裏面的會調中的參數重 NSDictionary *info 是否有cloudKey 來判斷是不是 iCloud 處理UI放到主線程 options.progressHandler = ^(double progress, NSError *error, BOOL *stop, NSDictionary *info){ isPhotoInICloud = YES;
}); //上面的進度下載完了以後纔會掉用resultHandler 回調 [[PHImageManager defaultManager] requestImageForAsset:asset targetSize:PHImageManagerMaximumSize contentMode:PHImageContentModeAspectFit options:options resultHandler:^(UIImage * _Nullable result, NSDictionary * _Nullable info) { if (isPhotoInICloud) { // Photo is in iCloud. } });