【问题标题】:PHImageManager requestImageForAsset returns nil sometimes for iCloud photosPHImageManager requestImageForAsset 有时会为 iCloud 照片返回 nil
【发布时间】:2015-09-11 07:44:49
【问题描述】:

大约 10% 的时间 PHImageManager.defaultManager().requestImageForAsset 在第一次返回有效但“降级”的 UIImage 后返回 nil 而不是有效的 UIImage。没有错误或我能看到的其他线索在带有 nil 的信息中返回。

这似乎发生在需要从 iCloud 下载的照片上,同时启用了 iCloud 照片库和优化 iPad 存储。我已尝试更改选项、大小等,但似乎没有任何影响。

如果我在失败后重试 requestImageForAsset,它通常会正确返回 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)")
           }
    }
}

【问题讨论】:

  • 我也遇到了同样的问题,你找到解决办法了吗?
  • 我看到了同样的问题。我认为这是一个错误并已提交错误报告。
  • 我在设置options.deliveryMode = .HighQualityFormat 时看到了这种情况,但在我设置options.deliveryMode = .Opportunistic 时却没有
  • @AlfieHanssen 很遗憾对我没有任何影响
  • 谁能告诉我这个stackoverflow.com/questions/55488998/…的解决方案?我也面临与视频相同的问题。

标签: ios photosframework


【解决方案1】:

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

[[PHImageManager defaultManager] requestImageForAsset: ...]

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


[[PHImageManager defaultManager] requestImageDataForAsset:...]

此功能仅适用于手机内存中的图片或您的应用最近从 iCloud 获取的图片。


这是一个有效的 sn-p,我正在使用 -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);
    }];

Full gist available on 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))")
    })

【讨论】:

  • 我不太明白这应该如何解决原来的问题?
  • 我同意 dlinsin,这看起来和我正在做的一样,但它仍然经常失败。
  • 我一直在使用它,从来没有失败过。它也可能是框架中的一个错误,因为它仍然不稳定。
  • 这不适用于 iCloud Drive 这是用于从 iCloud 照片库中获取照片
  • 棘手的部分是networkAccessAllowed。来自文档:“如有必要,将从 iCloud 下载图像”。默认情况下,它设置为 false。
【解决方案2】:

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

我没有找到解决办法,但受 @Nadzeya 启发的一个解决方法对我来说 100% 的时间是始终请求与资产大小相等的目标大小。。 p>

例如。

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 进行缩放,但至少在我的用例中,没有明显的性能问题,这比加载模糊或零图像要好得多。

此处可能的优化是,仅当图像返回为 nil 时,才以资源大小重新请求图像。

【讨论】:

【解决方案3】:

我尝试了很多东西

  • targetSize 大于 (400, 400):不起作用
  • targetSize 等于资产大小:无效
  • 在“设置”的 iCloud 照片中禁用 Optimize Storage:不起作用
  • requestImage 分派到后台队列:不起作用
  • 使用PHImageManagerMaximumSize:不起作用
  • 使用isNetworkAccessAllowed:不起作用
  • PHImageRequestOptions 中使用不同的值,例如versiondeliveryModeresizeMode:不起作用
  • 添加progressHandler:无效
  • 再次致电requestImage,如果失败:不起作用

我得到的只是零 UIImagePHImageResultDeliveredImageFormatKey 的信息,就像在这个雷达中 Photos Frameworks returns no error or image for particular assets

使用方面适合

什么对我有用,见https://github.com/hyperoslo/Gallery/blob/master/Sources/Images/Image.swift#L34

  • targetSize
  • 使用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】:

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

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

【讨论】:

  • 我目前在 iphone 11 pro、ios 13 上进行测试时遇到了类似的问题。options.version = .current 帮助了我,你!你救了我的命!
【解决方案5】:

上述方法对我没有用,但这个解决方案可以!

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
}

这里的区别在于递归重试。这对我有用 100% 的时间。

【讨论】:

  • options.isNetworkAccessAllowed = true 有帮助!谢谢! =)
  • options.isNetworkAccessAllowed = true 也解决了我的问题。
【解决方案6】:

尝试使用大于 (400, 400) 的 targetSize。 它帮助了我。

【讨论】:

【解决方案7】:

它对我有用的是让 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 */ }
        }
    }

【讨论】:

    【解决方案8】:

    我还收到了nil 的 iCloud 图片。如果我使用该方法的requestImagerequestImageData 风格,这并没有什么不同。事实证明,我的问题是我的设备通过 Charles Proxy 连接到网络,因为我想监控应用程序发出的请求和响应。出于某种原因,如果以这种方式连接,设备将无法与 iCloud 一起使用。一旦我关闭代理应用程序就可以获取 iCloud 图像。

    【讨论】:

      【解决方案9】:

      我的解决方案是设置 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     
          }
      }
      

      编码不错!

      【讨论】:

      【解决方案10】:

      以下解决了我的问题:

      let options = PHImageRequestOptions()
      options.isSynchronous = false
      options.isNetworkAccessAllowed = true
      options.deliveryMode = .opportunistic
      options.version = .current
      options.resizeMode = .exact
      

      【讨论】:

        【解决方案11】:

        从 iOS 14 开始,如果用户没有使用受限照片选择器授予使用特定照片的权限,也可能发生这种情况。欲了解更多信息,https://developer.apple.com/documentation/photokit/delivering_a_great_privacy_experience_in_your_photos_app

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2011-03-06
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-01-08
          • 2015-02-24
          • 1970-01-01
          相关资源
          最近更新 更多