Phimagemanager requestigeforasset renvoie zéro parfois pour les photos iCloud

environ 10% du temps PHImageManager.defaultManager ().requestImageForAsset retourne nil au lieu d'un UIImage valide après avoir retourné un UIImage valide bien que "dégradé". Aucune erreur ou autre indice que je peux voir est retourné dans l'info avec le néant.

cela semble se produire avec des photos qui doivent être téléchargées à partir d'iCloud, avec iCloud Photothèque et optimiser le stockage iPad les deux activées. J'ai essayé de changer les options, la taille, etc. mais rien ne semble question.

si je réessaie le requestie pour l'ensemble après l'échec, il sera généralement correctement retourner un UIImage, bien que parfois il nécessite un couple de réessais.

une idée de ce que je pourrais faire de mal? Ou est-ce juste un bug dans le cadre 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)")
           }
    }
}
35
demandé sur LenK 2015-06-25 00:58:25

7 réponses

je suis juste allé à travers ce aussi. Par mes tests, le problème apparaît sur les appareils qui ont l'option" Optimiser le stockage " activée et réside dans la différence entre les deux méthodes ci-dessous:

[[[PHImageManager defaultManager] requestigeforasset:...]

ceci récupérera avec succès les images iCloud distantes si vos options sont correctement configurées.


[[[PHImageManager defaultManager] requestImageDataForAsset:...]

cette fonction ne fonctionne que pour les images qui résident sur la mémoire des téléphones ou qui ont été récemment récupérées à partir d'iCloud par votre application sur une autre.


Voici un travail extrait de je suis aide -ours avec moi l'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);
    }];

Plein de gist disponible sur github

mise à jour pour Swift 3:

    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))")
    })
29
répondu ahbou 2017-06-28 18:38:47

j'ai trouvé que cela n'avait rien à voir avec le réseau ou iCloud. Il a parfois échoué, même sur des images qui étaient complètement locales. Parfois c'était des images de mon appareil photo, parfois c'était des images sauvées du web.

Je n'ai pas trouvé de solution, mais un travail autour inspiré par @Nadzeya qui a fonctionné 100% du temps pour moi était toujours demander une cible de taille égale à la taille de l'actif.

par exemple.

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

je crois que les inconvénients ce serait que nous obtenions l'image complète de nouveau dans la mémoire, et forçant ensuite L'ImageView à faire la mise à l'échelle, mais au moins dans mon cas d'utilisation, il n'y avait pas de problème de performance perceptible, et c'était beaucoup mieux que de charger une image floue ou nulle.

une optimisation possible ici est de requérir l'image à sa taille d'atout seulement si l'image revient comme nulle.

3
répondu Jeremy Lee 2017-03-18 05:57:11

j'ai essayé beaucoup de choses

  • targetSize supérieur (400, 400): pas de travail
  • targetSize égale à la taille de l'actif: ne fonctionne pas
  • Désactiver Optimize Storage dans iCloud Photos dans les Paramètres: pas de travail
  • Envoi requestImage à la file d'attente de fond: pas de travail
  • Utiliser PHImageManagerMaximumSize: non
  • Utiliser isNetworkAccessAllowed: non
  • Jouer avec les différentes valeurs PHImageRequestOptions, comme version,deliveryMode,resizeMode: non
  • Ajouter un progressHandler: non
  • Appel requestImage encore une fois il cas, il a échoué: pas de travail

Tout ce que j'obtiens est nul UIImage et info PHImageResultDeliveredImageFormatKey, comme dans ce radar photos Frameworks ne renvoie aucune erreur ou image pour des biens particuliers

utiliser aspect fit

Ce travail pour moi, voir https://github.com/hyperoslo/Gallery/blob/master/Sources/Images/Image.swift#L34

  • Utiliser targetSize avec < 200: c'est pourquoi je peux charger la miniature
  • Utiliser aspectFit: spécifier à contentMode le truc pour moi c'

Voici le code

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

Extraire de manière asynchrone

ce qui précède peut causer une condition de course, alors assurez-vous de récupérer de façon asynchrone, ce qui signifie non isSynchronous. Jetez un coup d'oeil à https://github.com/hyperoslo/Gallery/pull/72

2
répondu onmyway133 2017-11-29 14:48:05

Essayez d'utiliser targetSize supérieur (400, 400). Il m'a aidé.

1
répondu Nadzeya 2016-06-30 12:48:20

ce qui a fonctionné pour moi était de laisser PHImageManager charger les données de l'actif de façon synchrone, mais à partir d'un fil asynchrone de fond. Simplifié, il ressemble à ceci:

    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 */ }
        }
    }
0
répondu Fran Pugl 2017-08-22 10:08:40

j'avais aussi nil pour les images iCloud. Cela n'a pas fait de différence si j'ai utilisé requestImage ou requestImageData saveur de la méthode. Mon problème, il s'avère, était que mon appareil a été connecté au réseau via Charles Proxy depuis que je voulais surveiller les requêtes et les réponses app made. Pour une raison quelconque, le périphérique ne pourrait pas fonctionner avec iCloud s'il était connecté de cette façon. Une fois que j'aurais désactivé l'Application proxy pourrait obtenir des images iCloud.

0
répondu Nemanja Kovacevic 2018-03-20 00:05:57

la solution pour moi était de mettre un 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     
    }
}

Bon codage!

0
répondu Faustino Gagneten 2018-08-07 14:23:15