2014-10-09 11 views
15

Sto provando a visualizzare e salvare le immagini con Swift. Al primo colpo, mostra l'immagine remota su imageview, al secondo colpo mostra un'immagine vuota invece di un'immagine locale che viene salvata al primo colpo.Come salvare un'immagine remota con Swift?

var paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as String 
    var imagePath = paths.stringByAppendingPathComponent("images/\(id)/logo.jpg") 
    var checkImage = NSFileManager.defaultManager() 

    if (checkImage.fileExistsAtPath(imagePath)) { 
     let getImage = UIImage(contentsOfFile: imagePath) 
     self.image?.image = getImage 
    } else { 
     dispatch_async(dispatch_get_main_queue()) { 
      let getImage = UIImage(data: NSData(contentsOfURL: NSURL(string: remoteImage))) 
      UIImageJPEGRepresentation(getImage, 100).writeToFile(imagePath, atomically: true) 
      self.image?.image = getImage 
     } 
    } 

Modifica: questo ha funzionato per me.

var paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as String 
var dirPath = paths.stringByAppendingPathComponent("images/\(id)") 
var imagePath = paths.stringByAppendingPathComponent("images/\(id)/logo.jpg") 
var checkImage = NSFileManager.defaultManager() 

if (checkImage.fileExistsAtPath(imagePath)) { 
    let getImage = UIImage(contentsOfFile: imagePath) 
    self.image?.image = getImage 
} else { 
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)) { 
     checkImage.createDirectoryAtPath(dirPath, withIntermediateDirectories: true, attributes: nil, error: nil) 
     let getImage = UIImage(data: NSData(contentsOfURL: NSURL(string: remoteImage))) 
     UIImageJPEGRepresentation(getImage, 100).writeToFile(imagePath, atomically: true) 

     dispatch_async(dispatch_get_main_queue()) { 
      self.image?.image = getImage 
      return 
     } 
    } 
} 

risposta

18

Per rispondere alla domanda principale, si sta chiamando l'inizializzatore UIImage errato. Si dovrebbe essere chiamando UIImage(contentsOfFile: imagePath) in rapida 2 e UIImage(contentsOf: imagePath) in rapida 3.

Inoltre, sembra che si sta cercando di fare il vostro prendere remoto in background con dispatch_async (o DispatchQueue a Swift 3), ma si sta passando è la coda principale, quindi in realtà stai bloccando il thread principale/dell'interfaccia utente con quello. Si dovrebbe inviare ad uno degli scenari code invece e quindi inviare di nuovo alla coda principale quando effettivamente imposta l'immagine nel UI:

Swift 3:

DispatchQueue.global(qos: DispatchQoS.background.qosClass).async { 
    do { 
     let data = try Data(contentsOf: URL(string: self.remoteImage)!) 
     let getImage = UIImage(data: data) 
     try UIImageJPEGRepresentation(getImage!, 100)?.write(to: imagePath) 
     DispatchQueue.main.async { 
      self.image?.image = getImage 
      return 
     } 
    } 
    catch { 
      return 
    } 
} 

Swift 2:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)) { 
    let getImage = UIImage(data: NSData(contentsOfURL: NSURL(string: self.remoteImage))) 
    UIImageJPEGRepresentation(getImage, 100).writeToFile(imagePath, atomically: true) 

    dispatch_async(dispatch_get_main_queue()) { 
     self.image?.image = getImage 
     return 
    } 
} 

@Rob's answer re: recuperare l'immagine remota e salvarla è davvero il modo migliore per farlo.

+0

Grazie, l'ho cambiato in UIImage (contentsOfFile: imagePath).Ma quando cambio dispatch in dispatch_async (dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)) non viene visualizzata alcuna immagine remota. – bilmiyore

+0

Ho appena visto la modifica del tuo codice ... aggiornerò la mia risposta. –

+0

Grazie, penso che la spedizione funzioni meglio ora. Ma ancora non riesco a caricare le immagini localmente. Penso che "var imagePath = paths.stringByAppendingPathComponent (" images/\ (id) /logo.jpg ")" è il problema perché "se il blocco" non viene visualizzato affatto. – bilmiyore

10

Il codice che invia NSData(contentsOfURL:) (ora noto come Data(contentsOf:)) nella coda principale. Se hai intenzione di usare quel metodo sincrono per richiedere l'immagine remota, dovresti farlo su una coda di sfondo.

Inoltre, si sta prendendo l'NSData, convertendolo in un UIImage, e quindi la conversione di nuovo ad un NSData utilizzando UIImageJPEGRepresentation. Non effettuare il round-trip su UIImageJPEGRepresentation poiché si modifica il carico utile originale e si modifica la dimensione della risorsa. Basta solo confermano che i dati contenuti un'immagine, ma poi scrivono che originale NSData

Così, in Swift 3, probabilmente avrete bisogno di fare qualcosa di simile:

DispatchQueue.global().async { 
    do { 
     let data = try Data(contentsOf: URL(string: urlString)!) 
     if let image = UIImage(data: data) { 
      try data.write(to: fileURL) 
      DispatchQueue.main.async { 
       self.imageView?.image = image 
      } 
     } 
    } catch { 
     print(error) 
    } 
} 

Ancora meglio, si dovrebbe utilizzare NSURLSession perché si può diagnosticare meglio i problemi, è cancellabile, ecc. (E non usare il deprecato NSURLConnection). Vorrei anche controllare il statusCode della risposta. Per esempio:

func requestImage(_ url: URL, fileURL: URL) { 
    let task = URLSession.shared.dataTask(with: url) { data, response, error in 
     // check for fundamental network issues (e.g. no internet, etc.) 

     guard let data = data, error == nil else { 
      print("dataTask error: \(error?.localizedDescription ?? "Unknown error")") 
      return 
     } 

     // make sure web server returned 200 status code (and not 404 for bad URL or whatever) 

     guard let httpResponse = response as? HTTPURLResponse, 200 ..< 300 ~= httpResponse.statusCode else { 
      print("Error; Text of response = \(String(data: data, encoding: .utf8) ?? "(Cannot display)")") 
      return 
     } 

     // save image and update UI 

     if let image = UIImage(data: data) { 
      do { 
       // add directory if it doesn't exist 

       let directory = fileURL.deletingLastPathComponent() 
       try? FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true) 

       // save file 

       try data.write(to: fileURL, options: .atomic) 
      } catch let fileError { 
       print(fileError) 
      } 

      DispatchQueue.main.async { 
       print("image = \(image)") 
       self.imageView?.image = image 
      } 
     } 
    } 
    task.resume() 

} 

nota, la creazione just-in-time della cartella è necessario solo se non si è creato già. Personalmente, quando costruisco il percorso originale, creo la cartella lì anziché nel gestore di completamento, ma puoi farlo in qualsiasi modo tu voglia. Assicurati che la cartella esista prima di scrivere il file.

Indipendentemente da ciò, si spera che questo illustri i punti principali, vale a dire che dovresti salvare la risorsa originale e che dovresti farlo in background.

Per le interpretazioni di Swift 2, vedere previous revision of this answer.

+0

Ho appena provato questo, ma non ho potuto caricare alcuna immagine remota o locale. – bilmiyore

+1

@bilmiyore FYI, ho rivisto la mia risposta per migliorare la gestione degli errori per diagnosticare cosa non va, se non altro. In termini di caricamento dell'immagine locale, non l'ho toccato. – Rob

+0

Grazie a @Rob, anche questo ha funzionato. C'è solo un problema che non riesco a farlo funzionare con le sottodirectory. "\ (id) -logo.jpg" il percorso funziona ma "\ (id) \ logo.jpg" non lo è. – bilmiyore

Problemi correlati