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:
- If the cell being refreshed is at or below the horizon, the
UITableView
will not scroll
- if the cell being refreshed is above the top, the
UITableView
will not scroll
- 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.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…