2014-10-09 65 views
15

我試圖用Swift顯示和保存圖像。在第一次點擊時,它在imageview上顯示遠程圖像,第二次點擊它顯示空白imageview,而不是它應該是第一次點擊時保存的本地圖像。如何使用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 
     } 
    } 

編輯:這一個爲我工作。

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

回答

18

要回答你的主要問題,你打電話錯了UIImage初始值設定項。你應該迅速3.

被調用迅速2 UIImage(contentsOfFile: imagePath)UIImage(contentsOf: imagePath)此外,它看起來就像你正在試圖做的與dispatch_async(或SWIFT 3 DispatchQueue)背景的遠程讀取,但你傳遞它是主隊列,所以你實際上用它阻塞了主/ UI線程。你應該把它分派到後臺隊列,而不是一個,然後派遣回當你真正在你的UI設置圖像的主隊列:

斯威夫特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 
    } 
} 

斯威夫特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:取回你的遠程鏡像並保存它真的是最好的方法。

+0

謝謝,我將它改爲UIImage(contentsOfFile:imagePath)。但是當我將dispatch更改爲dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND,0))時,它甚至不顯示遠程圖像。 – bilmiyore 2014-10-09 19:07:09

+0

我剛剛看到編輯您的代碼...我會更新我的答案。 – 2014-10-09 19:10:06

+0

謝謝,我認爲派遣現在效果更好。但仍然無法在本地加載圖像。我認爲「var imagePath = paths.stringByAppendingPathComponent(」images/\(id)/logo.jpg「)」是問題,因爲「if block」根本不顯示。 – bilmiyore 2014-10-09 19:24:52

10

您的代碼將NSData(contentsOfURL:)(現在稱爲Data(contentsOf:))調度到主隊列中。如果您打算使用該同步方法來請求遠程圖像,則應該在後臺隊列上執行此操作。

此外,您正在採取NSData,將其轉換爲UIImage,然後使用UIImageJPEGRepresentation將其轉換回NSData。不要往返UIImageJPEGRepresentation,因爲您將更改原始有效負載並將更改資產的大小。纔剛剛確認數據包含的圖像,但隨後寫原NSData

因此,斯威夫特3,你可能想要做的事,如:

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

更妙的是,你應該因爲你用NSURLSession可以更好地診斷問題,可以取消等等(並且不要使用已棄用的NSURLConnection)。我還會檢查響應的statusCode。例如:

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() 

} 

請注意,僅在您尚未創建文件夾時才需要即時創建文件夾。就我個人而言,當我建立原始路徑時,我會在那裏創建文件夾而不是在完成處理程序中,但是您可以以任何您想要的方式執行此操作。在寫入文件之前,確保文件夾存在。

無論如何,希望這可以說明要點,即您應該保存原始資產,並且您應該在後臺執行此操作。

對於Swift 2演繹,請參閱previous revision of this answer

+0

我剛剛嘗試過,但無法加載任何遠程或本地圖像。 – bilmiyore 2014-10-09 19:22:01

+1

@bilmiyore僅供參考,我修改了我的答案以改善錯誤處理,以診斷出現錯誤的地方,如果有的話。就本地圖像加載而言,我沒有碰到這一點。 – Rob 2014-10-09 19:28:22

+0

感謝@Rob,那也工作。只有一個問題是我無法使它與子目錄一起工作。 「\(id)-logo.jpg」路徑正在工作,但「\(id)\ logo.jpg」不是。 – bilmiyore 2014-10-09 19:33:55