PHImageManager requestImageForAsset有时会为iCloud照片返回nil

Len*_*enK 45 ios photosframework

大约10%的时间PHImageManager.defaultManager().requestImageForAsset在首次返回有效但"降级"的UIImage后返回nil而不是有效的UIImage.没有错误或其他线索,我可以看到返回信息与零.

这似乎发生在需要从iCloud下载的照片上,同时启用了iCloud Photo Library和Optimize iPad Storage.我已经尝试过更改选项,尺寸等,但似乎没什么关系.

如果我在失败后重试requestImageForAsset,它通常会正确返回一个UIImage,虽然有时需要重试几次.

知道我可能做错了什么吗?或者它只是Photos框架中的一个错误?

    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)")
           }
    }
}
Run Code Online (Sandbox Code Playgroud)

ahb*_*bou 39

我也刚刚经历过这个.通过我的测试,问题出现在启用了"优化存储"选项的设备上,并且位于以下两种方法之间的差异:

[[PHImageManager defaultManager] requestImageForAsset:...]

如果您的选项配置正确,这将成功获取远程iCloud图像.


[[PHImageManager defaultManager] requestImageDataForAsset:...]

此功能仅适用于驻留在手机内存上的图像或最近由您的应用程序在任何其他内容上从iCloud中提取的图像.


这是我正在使用的一个工作片段 - 跟我一起玩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);
    }];
Run Code Online (Sandbox Code Playgroud)

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))")
    })
Run Code Online (Sandbox Code Playgroud)

  • 棘手的部分是`networkAccessAllowed`.来自doc:"如有必要,将从iCloud下载图像".默认情况下,它设置为false. (13认同)
  • 我真的不明白这应该如何解决原来的问题? (7认同)
  • **这不适用于iCloud Drive**用于从iCloud Photo Library中获取照片 (2认同)

Eyz*_*uky 9

上述方法对我都不起作用,但这个解决方案却有效!

private func getUIImage(asset: PHAsset, retryAttempts: Int = 10) -> UIImage? {
    var img: UIImage?
    let manager = PHImageManager.default()
    let options = PHImageRequestOptions()
    options.version = .original
    options.isSynchronous = true
    options.isNetworkAccessAllowed = true
    manager.requestImage(for: asset, targetSize: CGSize(width: asset.pixelWidth, height: asset.pixelHeight), contentMode: .aspectFit, options: options, resultHandler: { image, _ in
        img = image
    })
    if img == nil && retryAttempts > 0 {
        return getUIImage(asset: asset, retryAttempts: retryAttempts - 1)
    }
    return img
}
Run Code Online (Sandbox Code Playgroud)

这里的区别在于递归重试。这对我来说 100% 有效。

  • `options.isNetworkAccessAllowed = true` 有帮助!谢谢你!=) (3认同)
  • `options.isNetworkAccessAllowed = true` 也解决了我的问题。 (2认同)

Jer*_*emy 7

我发现这与网络或 iCloud 无关。它偶尔会失败,即使在完全本地化的图像上也是如此。有时是我相机的图像,有时是从网络上保存的图像。

我没有找到解决方法,但是受@Nadzeya 启发的一种解决方法对我来说 100% 的时间都有效,就是总是请求目标大小等于资产大小

例如。

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
        }
    });
Run Code Online (Sandbox Code Playgroud)

我相信这样做的缺点是我们将完整图像恢复到内存中,然后强制 ImageView 进行缩放,但至少在我的用例中,没有明显的性能问题,而且是比加载模糊或零图像要好得多。

这里可能的优化是仅当图像返回为 nil 时才以资产大小重新请求图像。


onm*_*133 6

我尝试了很多东西

  • targetSize 大于 (400, 400):不起作用
  • targetSize 等于资产大小:不起作用
  • Optimize Storage在“设置”中的“iCloud 照片”中禁用:不起作用
  • 调度requestImage到后台队列:不起作用
  • 使用PHImageManagerMaximumSize:不工作
  • 使用isNetworkAccessAllowed:不工作
  • 有不同的价值观发挥PHImageRequestOptions,比如versiondeliveryModeresizeMode:不工作
  • 添加一个progressHandler:不起作用
  • requestImage再次调用它失败的情况:不工作

我得到的只是 nilUIImage和 info with PHImageResultDeliveredImageFormatKey,就像在这个雷达中照片框架不返回特定资产的错误或图像

使用方面适合

什么对我有用,请参阅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
Run Code Online (Sandbox Code Playgroud)

异步获取

以上可能会导致竞争条件,因此请确保异步获取,这意味着没有isSynchronous。看看https://github.com/hyperoslo/Gallery/pull/72


nat*_*han 6

我也看到了这一点,唯一对我有用的就是设置options.isSynchronous = false。我的具体选择是:

options.isNetworkAccessAllowed = true
options.deliveryMode = .highQualityFormat
options.version = .current
options.resizeMode = .exact
options.isSynchronous = false
Run Code Online (Sandbox Code Playgroud)