UILabel text updated inside a Swift closure refuse

2019-01-27 00:33发布

问题:

While learning Swift, I am coding a simple practice iOS app to scrape weather info for a given city from a site and show it in a UILabel.

The code uses a "NSURLSession.sharedSession().dataTaskWithURL" closure. Although I'm able to fetch the data correctly and capture the relevant text in the "UILabel.text", I can't get the actual app to show the updated UILabel.

What am I doing wrong? Here is the relevant code:

@IBAction func buttonPressed(sender: AnyObject) {

   var urlString = "http://www.weather-forecast.com/locations/" + cityName.text.stringByReplacingOccurrencesOfString(" ", withString: "") + "/forecasts/latest"

   var url = NSURL(string: urlString)

   let task = NSURLSession.sharedSession().dataTaskWithURL(url) {(data, response, error) in

      var urlContent = NSString(data: data, encoding: NSUTF8StringEncoding) as String

      var contentArray = urlContent.componentsSeparatedByString("<span class=\"phrase\">")
      var weatherInfo = contentArray[1].componentsSeparatedByString("</span>")

      self.resultShow.text = weatherInfo[0] // Text does not show in the app

      println(weatherInfo[0]) // This works correctly
      println(self.resultShow.text) // This works correctly

   }

   task.resume()

}

回答1:

You need to perform your UI updates on the main thread

NSURLSession completion handlers will always be called on the background thread. To update your UI a simple dispatch_async to the main thread should suffice:)

@IBAction func buttonPressed(sender: AnyObject) {
   var urlString = "http://www.weather-forecast.com/locations/" + cityName.text.stringByReplacingOccurrencesOfString(" ", withString: "") + "/forecasts/latest"
   var url = NSURL(string: urlString)
   let task = NSURLSession.sharedSession().dataTaskWithURL(url) {(data, response, error) in

      var urlContent = NSString(data: data, encoding: NSUTF8StringEncoding) as String

      var contentArray = urlContent.componentsSeparatedByString("<span class=\"phrase\">")
      var weatherInfo = contentArray[1].componentsSeparatedByString("</span>")

      dispatch_async(dispatch_get_main_queue(), {
          //perform all UI stuff here        
          self.resultShow.text = weatherInfo[0] 
      })
   }
   task.resume()
}

EDIT

Whilst not important here since the closure is not retained, in some instances it's important to explicitly declare capture lists to avoid retain cycles.