Как определить, что PHAsset не полностью загружен из iCloud (поэтому мне нужно запросить снова с options.networkAccessAllowed)

Документы говорят:

PHImageResultIsInCloudKey: логическое значение, указывающее, хранятся ли данные фотоактива на локальном устройстве или их необходимо загрузить из iCloud. (NSNumber) Если ДА, изображение не было предоставлено, потому что данные актива должны быть загружены из iCloud. Чтобы загрузить данные, отправьте другой запрос и укажите YES для опции networkAccessAllowed.

Но этот ключ всегда равен YES, когда ресурс хранится в библиотеке фотографий iCloud, даже если он уже полностью загружен на устройство (загрузил его в моем приложении, а также открыл в приложении Photos).

Если изображение недоступно, я хочу дать пользователю возможность загрузить его (но не делайте это автоматически, по крайней мере, когда поблизости нет Wi-Fi).

Так как же узнать, нужно ли загружать изображение?

Еще более любопытно: когда мой блок результатовrequestImageForAsset:targetSize:contentMode:options:resultHandler:вызывается для изображения, которое должно быть загружено, я получаю последний вызов с requiredImage == nil, после того как была доставлена ​​уменьшенная версия.

В этом случае ухудшение - НЕТ, даже если у меня нет изображения, и изображение все еще нужно загружать из iCloud, так как пока только локальный эскиз из приложения "Фотографии" доступен локально.

Я проверял это на iPhone и iPad с различными версиями iOS 8 (8.1.x, 8.2 beta, 8.3 beta), поведение всегда одинаковое.

После того как я открыл изображение в приложении "Фотографии", последний вызов обработчика результатов будет иметь полноразмерное изображение, но PHImageResultIsInCloudKey все равно будет "ДА".

Вот некоторый код, как я запрашиваю изображения:

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
}];

4 ответа

Решение

Я могу подтвердить, что PHImageResultIsInCloudKey не является надежным. Для изображений, хранящихся в iCloud, возвращается 1, даже если исходное изображение было загружено на устройство. Это поведение отличается от документации, и я хотел бы предложить сообщить об ошибке на radar.apple.com. PhotoKit, на мой взгляд, все еще очень незрелый фреймворк - он содержит множество проблем, а также некоторые странные концептуальные решения.

Если вы позвоните requestImageDataForAsset: с networkAccessAllowed установлен в NO, вернулся imageData будет nil если клип еще не загружен из iCloud. В противном случае он вернет фактические данные, даже если клип хранится в 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
        }
    }
 }];

Вы можете использовать progressHandler, чтобы проверить, является ли PHAsset из 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.
});

Лучший способ проверить, находится ли PHAsset в облаке, состоит в том, чтобы попытаться получить к нему доступ через PHImageManager с networkAccessAllowed = NO, и если вы не получаете изображение / актив, то вы знаете, что он все еще находится в облаке (и он выиграл не скачивается):

    + (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);
    }
}

На всякий случай кто-то наткнется на это при попытке исправить ошибку в iOS 13.

Метод в этом потоке УСТАРЕЛ с iOS 8.

В iOS 13 вам нужно вместо этого вызвать этот метод:

- (PHImageRequestID)requestImageDataAndOrientationForAsset:(PHAsset *)asset 
                                                   options:(PHImageRequestOptions *)options 
                                             resultHandler:(void (^)(NSData *imageData, NSString *dataUTI, CGImagePropertyOrientation orientation, NSDictionary *info))resultHandler;

https://developer.apple.com/documentation/photokit/phimagemanager/3237282-requestimagedataandorientationfo

Другие вопросы по тегам