Подтвердить что ты не робот

PHImageManager requestImageForAsset возвращает ноль иногда для фотографий iCloud

Примерно в 10% случаев PHImageManager.defaultManager(). requestImageForAsset возвращает nil вместо действительного UIImage после первого возврата действительного, хотя и "деградированного" UIImage. Никакая ошибка или другая подсказка, которую я вижу, не возвращаются в информации с нулем.

Это похоже на фотографии, которые необходимо загрузить из iCloud, с включенной iCloud Photo Library и Optimize iPad Storage. Я попытался изменить параметры, размер и т.д., Но ничего не имеет значения.

Если я повторю запрос requestMageForAsset после сбоя, он обычно правильно вернет UIImage, хотя иногда ему требуется несколько попыток.

Любая идея, что я могу делать неправильно? Или это просто ошибка в фреймворке?

    func photoImage(asset: PHAsset, size: CGSize, contentMode: UIViewContentMode, completionBlock:(image: UIImage, isPlaceholder: Bool) -> Void) -> PHImageRequestID? {

    let options = PHImageRequestOptions()
    options.networkAccessAllowed = true
    options.version = .Current
    options.deliveryMode = .Opportunistic
    options.resizeMode = .Fast

    let requestSize = !CGSizeEqualToSize(size, CGSizeZero) ? size : PHImageManagerMaximumSize
    let requestContentMode = contentMode == .ScaleAspectFit ? PHImageContentMode.AspectFit : PHImageContentMode.AspectFill

    return PHImageManager.defaultManager().requestImageForAsset(asset, targetSize: requestSize, contentMode: requestContentMode, options: options)
        { (image: UIImage!, info: [NSObject : AnyObject]!) in
            if let image = image {
                let degraded = info[PHImageResultIsDegradedKey] as? Bool ?? false
                completionBlock(image: photoBlock.rotatedImage(image), isPlaceholder: degraded)

            } else {
                let error = info[PHImageErrorKey] as? NSError
                NSLog("Nil image error = \(error?.localizedDescription)")
           }
    }
}
4b9b3361

Ответ 1

Я тоже прошел через это. По моим тестам проблема появляется на устройствах с включенной опцией "Оптимизировать хранение" и находится в разнице между двумя следующими способами:

[[PHImageManager defaultManager] requestImageForAsset:...]

Это позволит успешно получить удаленные изображения iCloud, если ваши параметры настроены правильно.


[[PHImageManager defaultManager] requestImageDataForAsset:...]

Эта функция работает только для изображений, которые находятся в памяти телефона или которые недавно были получены из iCloud вашим приложением на любом другом.


Вот рабочий фрагмент, который я использую -bear со мной Obj-c :)

 PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init];
 options.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat; //I only want the highest possible quality
 options.synchronous = NO;
 options.networkAccessAllowed = YES;
 options.progressHandler = ^(double progress, NSError *error, BOOL *stop, NSDictionary *info) {
        NSLog(@"%f", progress); //follow progress + update progress bar
    };

  [[PHImageManager defaultManager] requestImageForAsset:myPhAsset targetSize:self.view.frame.size contentMode:PHImageContentModeAspectFill options:options resultHandler:^(UIImage *image, NSDictionary *info) {
        NSLog(@"reponse %@", info);
        NSLog(@"got image %f %f", image.size.width, image.size.height);
    }];

Полный доступ к github

Обновлено для Swift 4:

    let options = PHImageRequestOptions()
    options.deliveryMode = PHImageRequestOptionsDeliveryMode.highQualityFormat
    options.isSynchronous = false
    options.isNetworkAccessAllowed = true

    options.progressHandler = {  (progress, error, stop, info) in
        print("progress: \(progress)")
    }

    PHImageManager.default().requestImage(for: myPHAsset, targetSize: view.frame.size, contentMode: PHImageContentMode.aspectFill, options: options, resultHandler: {
     (image, info) in
        print("dict: \(String(describing: info))")
        print("image size: \(String(describing: image?.size))")
    })

Ответ 2

Я обнаружил, что это не имеет ничего общего с сетью или iCloud. Иногда это случалось, даже на изображениях, которые были полностью локальными. Иногда это были изображения с моей камеры, иногда это было бы из изображений, сохраненных из Интернета.

Я не нашел исправления, но работа, вдохновленная @Nadzeya, которая работала в 100% случаев для меня, была , чтобы всегда запрашивать размер цели, равный размеру активов.

Eg.

PHCachingImageManager().requestImage(for: asset, 
                              targetSize: CGSize(width: asset.pixelWidth, height: asset.pixelHeight) , 
                             contentMode: .aspectFit, 
                                 options: options, 
                           resultHandler: { (image, info) in
        if (image == nil) {
            print("Error loading image")
            print("\(info)")
        } else {
            view.image = image
        }
    });

Я считаю, что недостатками этого было бы то, что мы вернем полное изображение в память, а затем заставим ImageView делать масштабирование, но, по крайней мере, в моем случае, не было заметной проблемы с производительностью, и это было намного лучше, чем загрузка размытого или нулевого изображения.

Возможная оптимизация здесь заключается в том, чтобы повторно запрашивать изображение при его размере активов, только если изображение возвращается как ноль.

Ответ 3

Я пробовал много вещей

  • targetSize больше (400, 400): не работает
  • targetSize равен размеру актива: не работает
  • Отключить Optimize Storage в iCloud Фото в настройках: не работать
  • Отправка requestImage в фоновый режим: не работает
  • Использовать PHImageManagerMaximumSize: не работать
  • Использовать isNetworkAccessAllowed: не работать
  • Играйте с разными значениями в PHImageRequestOptions, например version, deliveryMode, resizeMode: not work
  • Добавить progressHandler: не работать
  • Вызов requestImage снова в случае сбоя: не работает

Все, что я получаю, это nil UIImage и информация с PHImageResultDeliveredImageFormatKey, как в этом радаре Фотографий Frameworks не возвращает никаких ошибок или изображений для определенных активов

Использовать аспект

Какая работа для меня, см. https://github.com/hyperoslo/Gallery/blob/master/Sources/Images/Image.swift#L34

  • Используйте targetSize с < 200: вот почему я могу загрузить миниатюру
  • Использовать aspectFit: Указать contentMode делает трюк для меня

Вот код

let options = PHImageRequestOptions()
options.isSynchronous = true
options.isNetworkAccessAllowed = true

var result: UIImage? = nil

PHImageManager.default().requestImage(
  for: asset,
  targetSize: size,
  contentMode: .aspectFit,
  options: options) { (image, _) in
    result = image
}

return result

Извлечение асинхронно

Вышеуказанное может привести к состоянию гонки, поэтому убедитесь, что вы выбрали асинхронно, что означает isSynchronous. Взгляните на https://github.com/hyperoslo/Gallery/pull/72

Ответ 4

Попробуйте использовать targetSize больше (400, 400). Это помогло мне.

Ответ 5

Для меня это помогло PHImageManager загружать данные об активах синхронно, но из асинхронного фонового потока. Упрощенное выражение выглядит так:

    DispatchQueue.global(qos: .userInitiated).async {
        let requestOptions = PHImageRequestOptions()
        requestOptions.isNetworkAccessAllowed = true
        requestOptions.version = .current
        requestOptions.deliveryMode = .opportunistic
        requestOptions.isSynchronous = true
        PHImageManager.default().requestImage(for: asset, targetSize: size, contentMode: .aspectFit, options: requestOptions) { image, _ in
            DispatchQueue.main.async { /* do something with the image */ }
        }
    }

Ответ 6

Я также получал nil за изображения iCloud. Это не имело значения, если я использовал requestImage или requestImageData этого метода. Оказывается, моя проблема заключалась в том, что мое устройство было подключено к сети через Charles Proxy, так как я хотел отслеживать запросы и отзывы. По какой-то причине устройство не могло работать с iCloud при подключении таким образом. Как только я выключил приложение прокси, можно получить изображения iCloud.

Ответ 7

Решение для меня заключалось в установке targetSize

var image: UIImage?
let options = PHImageRequestOptions()
options.isNetworkAccessAllowed = true
options.isSynchronous = true
options.resizeMode = PHImageRequestOptionsResizeMode.exact

let targetSize = CGSize(width:1200, height:1200)

PHImageManager.default().requestImage(for: self, targetSize: targetSize, contentMode: PHImageContentMode.aspectFit, options: options) { (receivedImage, info) in

    if let formAnImage = receivedImage
    {
        image = formAnImage     
    }
}

Хорошая кодировка!

Ответ 8

Я также видел это, и единственное, что работало для меня, было установка options.isSynchronous = false. Мои конкретные варианты:

options.isNetworkAccessAllowed = true
options.deliveryMode = .highQualityFormat
options.version = .current
options.resizeMode = .exact
options.isSynchronous = false