Updating cell height after image downloads

后端 未结 1 778
执笔经年
执笔经年 2020-12-15 06:34

I am displaying some text and images in a UITableView. The image first gets downloaded. Since before the image gets downloaded, I don\'t know the size of image,

相关标签:
1条回答
  • 2020-12-15 07:07

    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.

    0 讨论(0)
提交回复
热议问题