2015-06-24 12 views
30

Circa il 10% delle volte PHImageManager.defaultManager(). RequestImageForAsset restituisce nil invece di una UIImage valida dopo aver restituito una UIImage valida, sebbene "degradata". Nessun errore o altro indizio che posso vedere viene restituito nelle informazioni con il nullo.PHImageManager requestImageForAsset restituisce a volte nil per le foto di iCloud

Questo sembra accadere con le foto che devono essere scaricate da iCloud, con iCloud Photo Library e Optimize iPad Storage entrambi abilitati. Ho provato a cambiare le opzioni, le dimensioni, ecc. Ma nulla sembra importare.

Se ritento la richiestaImageForAsset dopo l'errore, in genere restituirà correttamente un UIImage, anche se a volte richiede un paio di tentativi.

Qualche idea su cosa potrei fare male? O è solo un bug nel framework 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)") 
      } 
    } 
} 
+0

Ho esattamente lo stesso problema, hai trovato qualche soluzione per questo? – dlinsin

+1

Sto vedendo lo stesso problema. Credo che sia un bug e abbia presentato una segnalazione di bug. – Joey

+3

Sto vedendo questo accadere quando ho impostato 'options.deliveryMode = .HighQualityFormat' ma non quando ho impostato' options.deliveryMode = .Opportunistic' –

risposta

18

Ho appena passato anche questo. Con i miei test il problema sembra sui dispositivi che hanno l'opzione "ottimizzare l'archiviazione" abilitati e risiede nella differenza tra i due metodi sotto:

[[PHImageManager defaultManager] requestImageForAsset: ...]

Questo recupererà correttamente le immagini di iCloud remote se le tue opzioni sono configurate correttamente.


[[PHImageManager defaultManager] requestImageDataForAsset: ...]

Questa funzione è disponibile solo per le immagini che si trovano nella memoria telefoni o che sono stati recentemente recuperati da iCloud per la vostra applicazione su qualsiasi altro .


Ecco un frammento di lavoro che sto utilizzando -bear con me 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); 
    }]; 

Full gist available on github

Aggiornato per 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))") 
    }) 
+4

Non capisco davvero come si suppone per risolvere il problema originale? – dlinsin

+1

Sono d'accordo con dlinsin, sembra lo stesso di quello che sto facendo e fallisce regolarmente. – LenK

+1

L'ho usato e non ho mai avuto esito negativo. Potrebbe anche essere un bug nel framework poiché è ancora instabile. – ahbou

1

Provare a utilizzare targetSize maggiore di (400, 400). Mi ha aiutato.

1

Ho scoperto che questo non aveva nulla a che fare con la rete o iCloud. Occasionalmente falliva, anche su immagini completamente locali. A volte erano immagini dalla mia macchina fotografica, a volte venivano da immagini salvate dal web.

non ho trovato una soluzione, ma un lavoro in giro ispirato @Nadzeya che ha lavorato al 100% del tempo per me è stato per richiedere sempre una dimensione di destinazione pari alla dimensione patrimoniale.

Es.

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

Credo che lo svantaggi di questo sarebbe che stiamo ottenendo l'immagine completa indietro nella memoria, e poi costringendo l'ImageView a fare la scala, ma almeno nel mio caso d'uso, non c'era una problema di prestazioni evidente, ed era molto meglio che caricare un'immagine sfocata o nulla.

Un possibile ottimizzazione qui consiste nel richiedere nuovamente l'immagine in base alla dimensione dell'asset solo se l'immagine ritorna come zero.

0

Ciò che ha funzionato per me è stato consentire a PHImageManager di caricare i dati dell'asset in modo sincrono, ma da un thread di background asincrono. Semplificata sembra che questo:

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

'ho provato molte cose

  • TargetSize superiore (400, 400): non funziona
  • TargetSize equivale a dimensione patrimoniale: non funziona
  • Disattiva Optimize Storage in iCloud Foto in Impostazioni: non funziona
  • Invio requestImage alla coda di sfondo: non funziona
  • Usa PHImageManagerMaximumSize: non funziona
  • Uso isNetworkAccessAllowed: non funziona
  • Gioca con valori diversi nel PHImageRequestOptions, come version, deliveryMode, resizeMode: non funzionare
  • Aggiungi un progressHandler: non funziona
  • chiamata requestImage di nuovo casi non riusciti: non funziona

Tutto ciò che ottengo è nil UIImage e informazioni con PHImageResultDeliveredImageFormatKey, come in questo radar Photos Frameworks returns no error or image for particular assets

Usa aspetto rientra

Che lavoro per me, vedere https://github.com/hyperoslo/Gallery/blob/master/Sources/Images/Image.swift#L34

  • Usa targetSize con < 200: questo è il motivo per cui posso caricare la miniatura
  • Utilizzare aspectFit: specificare su contentMode il trucco per me

Ecco il codice

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 

Fetch in modo asincrono

È possibile che questo può causare condizione di gara, quindi assicuratevi si recupera in modo asincrono, il che significa che non isSynchronous. Date un'occhiata a https://github.com/hyperoslo/Gallery/pull/72

Problemi correlati