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)")
}
}
}
Ответы
Ответ 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