58

およそ 10% の確率で、PHImageManager.defaultManager().requestImageForAsset は、有効ではあるが「劣化した」UIImage を最初に返した後、有効な UIImage ではなく nil を返します。私が見ることができるエラーやその他の手がかりは、nil の情報には返されません。

これは、iCloud Photo Library と Optimize iPad Storage の両方が有効になっている場合に、iCloud からダウンロードする必要がある写真で発生するようです。オプションやサイズなどを変更してみましたが、何も問題ないようです。

失敗した後に 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)")
           }
    }
}
4

11 に答える 11

52

私もちょうどこれを経験しました。私のテストでは、この問題は「ストレージの最適化」オプションが有効になっているデバイスで発生し、以下の 2 つの方法の違いにあります。

[[PHImageManager defaultManager] requestImageForAsset: ...]

オプションが正しく設定されていれば、リモートの iCloud イメージを正常に取得できます。


[[PHImageManager defaultManager] requestImageDataForAsset:...]

この関数は、電話のメモリに存在する画像、または他のアプリで最近 iCloud からフェッチされた画像に対してのみ機能します。


これが私が使用している作業スニペットです -bear with me the 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))")
    })
于 2015-08-03T10:23:58.940 に答える
7

これはネットワークやiCloudとは何の関係もないことがわかりました。完全にローカルな画像であっても、時々失敗しました。カメラからの画像の場合もあれば、Web から保存した画像の場合もありました。

修正は見つかりませんでしたが、@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 にスケーリングを強制することだと思いますが、少なくとも私の使用例では、顕著なパフォーマンスの問題はありませんでした。ぼやけたまたはゼロの画像をロードするよりもはるかに優れています。

ここで考えられる最適化は、画像が nil として戻ってきた場合にのみ、アセット サイズで画像を再リクエストすることです。

于 2017-03-18T05:57:11.283 に答える
6

私もこれを見ていましたが、私のために働いたのは設定だけでしたoptions.isSynchronous = false。私の特定のオプションは次のとおりです。

options.isNetworkAccessAllowed = true
options.deliveryMode = .highQualityFormat
options.version = .current
options.resizeMode = .exact
options.isSynchronous = false
于 2019-01-15T20:50:34.880 に答える
2

(400, 400) より大きい targetSize を使用してみてください。それは私を助けました。

于 2016-06-30T12:48:20.337 に答える
1

以下は私の問題を修正しました:

let options = PHImageRequestOptions()
options.isSynchronous = false
options.isNetworkAccessAllowed = true
options.deliveryMode = .opportunistic
options.version = .current
options.resizeMode = .exact
于 2019-11-18T22:24:49.237 に答える