PHImageManager requestImageForAsset有时会为iCloud照片

时间:2015-06-24 21:58:25

标签: 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)")
           }
    }
}

11 个答案:

答案 0 :(得分:36)

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

[[PHImageManager defaultManager] requestImageForAsset:...]

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

[[PHImageManager defaultManager] requestImageDataForAsset:...]

此功能仅适用于手机内存中的图像或最近由您的应用程序从iCloud中提取的图像。

这是我正在使用的一个工作片段 - 跟我一起玩Obj-c:)

var timestamp = new Date(data.responsecusthistory.created_at);
var date      = timestamp.toDateString(); 

Full gist available on github

针对Swift 4进行了更新:

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

答案 1 :(得分:5)

我发现这与网络或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
        }
    });

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

此处可能的优化是仅在图片以零返回时才以图片的资产大小重新请求图片。

答案 2 :(得分: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与<一起使用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

答案 3 :(得分:2)

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

答案 4 :(得分:1)

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

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

答案 5 :(得分:1)

以上内容对我无用,但此解决方案确实有效!

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%的机会。

答案 6 :(得分:0)

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

答案 7 :(得分:0)

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

答案 8 :(得分:0)

对我来说,解决方案是设置 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     
    }
}

好的编码!

答案 9 :(得分:0)

以下内容解决了我的问题:

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

答案 10 :(得分:0)

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