UIImageView in table view not showing until clicke

2019-07-21 00:12发布

问题:

I am in the process of learning swift/IOS and having a problem with getting an image to display in a table view cell when the imageView is set within a closure passed to dispatch_async(dispatch_get_main_que).

Here is my code for returning the cell for the tableview. Notice that the dispatch calls are commented out. In this state everything is OK. (the image is displayed with out any interaction)

  override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell: UITableViewCell
    if indexPath.section == 3 {
      cell = tableView.dequeueReusableCellWithIdentifier("imageCell", forIndexPath: indexPath) as! MentionedImageTableViewCell
      if let images = tweetSections?[3].items as? [MediaItem] {
        let imageUrl = images[indexPath.row].url
        let qos = Int(QOS_CLASS_USER_INTERACTIVE.value)
        let queue = dispatch_get_global_queue(qos, 0)
        println("feching image for cell = \(cell)")
//      dispatch_async(queue) {
          if let imageData = NSData(contentsOfURL: imageUrl), let image = UIImage(data: imageData) {
            println("fetch complete")
//          dispatch_async(dispatch_get_main_queue()) {
              println("posting image = \(image), to cell = \(cell)")
              cell.imageView?.image = image
              println("cells image = \(cell.imageView?.image)")
//          }
          }
//      }
      }
    } else {
      cell = tableView.dequeueReusableCellWithIdentifier("textCell", forIndexPath: indexPath) as! UITableViewCell
      if let keywords = tweetSections![indexPath.section].items as? [Tweet.IndexedKeyword] {
        let text = keywords[indexPath.row].keyword
        cell.textLabel?.text = text
      }
    }
    return cell
  }

But when I wrap the update in a closure passed to dispatch_async(get_main_que() {}, then the image does not appear until after I click on it or rotate the device Notice that the only difference between this code and the above is two lines that are un-commented. Note; This is all on the simulator

  override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell: UITableViewCell
    if indexPath.section == 3 {
      cell = tableView.dequeueReusableCellWithIdentifier("imageCell", forIndexPath: indexPath) as! MentionedImageTableViewCell
      if let images = tweetSections?[3].items as? [MediaItem] {
        let imageUrl = images[indexPath.row].url
        let qos = Int(QOS_CLASS_USER_INTERACTIVE.value)
        let queue = dispatch_get_global_queue(qos, 0)
        println("feching image for cell = \(cell)")
//      dispatch_async(queue) {
          if let imageData = NSData(contentsOfURL: imageUrl), let image = UIImage(data: imageData) {
            println("fetch complete")
            dispatch_async(dispatch_get_main_queue()) {
              println("posting image = \(image), to cell = \(cell)")
              cell.imageView?.image = image
              println("cells image = \(cell.imageView?.image)")
            }
          }
//      }
      }
    } else {
      cell = tableView.dequeueReusableCellWithIdentifier("textCell", forIndexPath: indexPath) as! UITableViewCell
      if let keywords = tweetSections![indexPath.section].items as? [Tweet.IndexedKeyword] {
        let text = keywords[indexPath.row].keyword
        cell.textLabel?.text = text
      }
    }
    return cell
  }

回答1:

If somehow you have to put the image assignment into the GCD(maybe for asynchronous image loading), you have to call the following methods, the second one is optional depends on your desire of update frequency:

cell.setNeedsLayout() //invalidate current layout
cell.layoutIfNeeded() //update immediately

I guess the |tableView:cellForRowAtIndexPath:| calls the above methods implicitly. So in most cases you don't have to care about that. But if you do it asynchronously, the time the above data source method calls these methods, there is no image to show at all. So sometimes later, you get the image, you have to call it explicitly after image assignment to get proper view updates.

After some testing, the data source method simply calls two methods |setNeedsDisplay| only once if not called before and then |setNeedsLayout| couple of times