Updating cell height after image downloads

三世轮回 提交于 2019-11-28 23:29:44

Short Answer

  • Give enough breathing space to estimatedRowHeight
  • Changing a UITableViewCell once returned by dequeueReusableCellWithIdentifier will not work with cached cells
  • Trigger a single cell reload with reloadRowsAtIndexPaths
  • Manage your cache with Core Data and let NSFetchedResultsController boilerplate code can do all the UI work.

In Details

No unexpected scroll, only updates images as they come:

  1. If the cell being refreshed is at or below the horizon, the UITableView will not scroll
  2. if the cell being refreshed is above the top, the UITableView will not scroll
  3. the UITableView will only scroll when the cell is in plain sight, and requires more space than available.

Let UITableViewAutomaticDimension do the hard work

You need to tell Cocoa Touch that the cell is stale, so that it will trigger a new dequeueReusableCellWithIdentifier, to which you are going to return a cell with the proper height.
Short of reloading the entire table view or one of its section, and assuming that your indexes are stable, invoke -tableView:reloadRows:at:with: passing the indexPath of the cell that just changed, and a .fade animation.

Code:

override func viewDidLoad() {
    super.viewDidLoad()
    tableView.estimatedRowHeight = 250 // match your tallest cell
    tableView.rowHeight = UITableViewAutomaticDimension
}

Use URLSession. When an image becomes available, fire reloadRows:at:with:

func loadImage(_ url: URL, indexPath: IndexPath) {
    let downloadTask:URLSessionDownloadTask =
        URLSession.shared.downloadTask(with: url, completionHandler: {
        (location: URL?, response: URLResponse?, error: Error?) -> Void in
        if let location = location {
            if let data:Data = try? Data(contentsOf: location) {
                if let image:UIImage = UIImage(data: data) {
                    self.cachedImages[indexPath.row] = image // Save into the cache
                    DispatchQueue.main.async(execute: { () -> Void in
                        self.tableView.beginUpdates()
                        self.tableView.reloadRows(
                            at: [indexPath],
                            with: .fade)
                        self.tableView.endUpdates()
                    })
                }
            }
        }
    })
    downloadTask.resume()
}

Once in the cache, cellForRow merely reads into it from the UI thread:

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
    let cell = tableView.dequeueReusableCell(withIdentifier: "id") as! CustomCell
    cell.imageView.image = cachedImages[indexPath.row]      // Read from the cache
    return cell
}

Example: fetch a random set of images from *Wikipedia*

► Find this solution on GitHub and additional details on Swift Recipes.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!