问题
The docs say:
PHImageResultIsInCloudKey: A Boolean value indicating whether the photo asset data is stored on the local device or must be downloaded from iCloud. (NSNumber) If YES, no image was provided, because the asset data must be downloaded from iCloud. To download the data, submit another request, and specify YES for the networkAccessAllowed option.
But this key is always YES when a asset is stored in the iCloud Photo Library, even when it is already completely downloaded to the device (downloaded it in my app, also opened it in the Photos app).
If an image is not available I want to give the user a possibility to download it (but don't do it automatically, at least not when no Wifi is around).
So how do I find out if the image needs to be downloaded?
Even more curious: When my result block of
requestImageForAsset:targetSize:contentMode:options:resultHandler:
is called for an image that needs to be downloaded, I get a last call with requestedImage == nil, after a smaller and degraded version was delivered.
In this case degraded is NO, even that I got no image and the image still has to be downloaded from iCloud as only a small thumbnail from the Photos app is locally available so far.
I tested this on iPhones and iPads with different iOS 8 versions (8.1.x, 8.2 beta, 8.3 beta) the behavior is always the same.
Once I opened the image in the Photos app, the last call of the result handler has the full size image, but PHImageResultIsInCloudKey will still be YES.
Here's some code how I request the images:
PHImageRequestOptions *options = [[PHImageRequestOptions alloc]init];
options.deliveryMode = PHImageRequestOptionsDeliveryModeOpportunistic;
options.networkAccessAllowed = NO;
[self.imageManager requestImageForAsset:asset targetSize:size contentMode:PHImageContentModeAspectFill options:options resultHandler:^(UIImage *requestedImage, NSDictionary *info) {
// Checking for requestedImage and the info keys here
// When a full sized image was loaded, the result of PHImageResultIsInCloudKey is still YES
// When a full sized image couldn't be loaded cause it's in the cloud, isDegraded is NO and PHImageResultIsInCloudKey is YES (as always) and requestedImage is nil
}];
回答1:
I can confirm that PHImageResultIsInCloudKey is not reliable. For images stored in iCloud it returns 1, even though the original image has been downloaded to the device. This behavior is in contrast to the documentation and I would suggest to report a bug at radar.apple.com. PhotoKit in my opinion is still a very immature framework - it contains lots of issues and also some weird conceptional decisions.
回答2:
If you call requestImageDataForAsset:
with networkAccessAllowed
set to NO
, the returned imageData
will be nil
if the clip is not already downloaded from iCloud. Otherwise it will return the actual data, even though the clip is stored in iCloud.
PHImageManager *manager = [PHImageManager defaultManager];
PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init];
options.networkAccessAllowed = NO;
[manager
requestImageDataForAsset:asset
options:options
resultHandler:^(NSData * _Nullable imageData, NSString * _Nullable dataUTI, UIImageOrientation orientation, NSDictionary * _Nullable info) {
if ([[info valueForKey:PHImageResultIsInCloudKey] boolValue]) {
// Image is in iCloud
if (imageData) {
// Image is downloaded
} else {
// Image is not downloaded
}
}
}];
回答3:
You can use the progressHandler to check whether the PHAsset is from iCloud.
__block BOOL isPhotoInICloud = NO;
PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init];
options.progressHandler = ^(double progress, NSError *error, BOOL *stop, NSDictionary *info){
isPhotoInICloud = YES;
// some code to update the download progress
});
options.networkAccessAllowed = YES;
options.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat;
options.synchronous = NO;
[[PHImageManager defaultManager] requestImageForAsset:asset targetSize:PHImageManagerMaximumSize contentMode:PHImageContentModeAspectFit options:options resultHandler:^(UIImage * _Nullable result, NSDictionary * _Nullable info) {
// use the options to get the high quality image for only once time.
});
回答4:
The best way to check if a PHAsset is still in the cloud is to try and access it through PHImageManager with networkAccessAllowed = NO, and if you don't get the image/asset, then you know it's still in the cloud (and it won't be downloaded):
+ (void)checkCloudStatusForPHAsset:(PHAsset*)phAsset completion:(void (^)(BOOL isInCloud))completionBlock
{
if (phAsset) {
if (phAsset.mediaType == PHAssetMediaTypeVideo) {
PHVideoRequestOptions *options = [[PHVideoRequestOptions alloc] init];
options.version = PHVideoRequestOptionsVersionOriginal;
options.deliveryMode = PHVideoRequestOptionsDeliveryModeHighQualityFormat;
options.networkAccessAllowed = NO;
[[PHImageManager defaultManager] requestAVAssetForVideo:phAsset options:options resultHandler:^(AVAsset * _Nullable asset, AVAudioMix * _Nullable audioMix, NSDictionary * _Nullable info) {
completionBlock(asset == nil);
}];
}
else if (phAsset.mediaType == PHAssetMediaTypeImage) {
PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init];
options.version = PHImageRequestOptionsVersionOriginal;
options.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat;
options.resizeMode = PHImageRequestOptionsResizeModeNone;
options.networkAccessAllowed = NO;
[[PHImageManager defaultManager] requestImageDataForAsset:phAsset options:options resultHandler:^(NSData * _Nullable imageData, NSString * _Nullable dataUTI, UIImageOrientation orientation, NSDictionary * _Nullable info) {
completionBlock(imageData == nil);
}];
}
else {
completionBlock(NO);
}
}
else {
completionBlock(NO);
}
}
来源:https://stackoverflow.com/questions/28482275/how-to-identify-if-a-phasset-is-not-completely-downloaded-from-icloud-so-i-need