This question is couple of years old, but maybe this answer can be useful to someone else with a similar problem, like me before to find the right solution.
Since the images are loaded asynchronously, if we didn't provide a fixed height for the UIIMageView we have to force a cell update when the download is finished. This because cell updating (i.e. AutoLayout Constraints recalculation) is done automatically only after cellForRowAt
method, that is called when a cell is displayed for the first time, or when the table is scrolled to show other cells. In both cases probably the images are not yet downloaded by the af_setImage()
method, so nothing but the placeholder will be displayed since their sizes are unknown for the moment.
To force a cell update we need to use beginUpdates()
and endUpdates()
methods, putting them inside the completion handler of .af_setImage()
. This way, every time the downloading is completed, the cell will be updated.
But, to avoid a loop, before to call beginUpdates()
/endUpdates()
we have to check if we have already update the cell before, because by calling these methods, the cellForRowAt
method is called again and consequently the af_setImage()
and its completion closure with beginUpdates()
/endUpdates()
inside it).
This means that we have to update the cell only when the download is just finished, and not when the image is already cashed (because, if it is cashed, it means that we have already updated the cell). This can be accomplished by checking the response of the completion handler: if it is not nil
, the image was just downoladed, if it is nil
, the image was cashed.
As a side benefit the cell height will be automagically adjusted (remember to put tableView.estimatedRowHeight = 400
and tableView.rowHeight = UITableViewAutomaticDimension
in your viewDidLoad()
method)
Finally, here it is the code:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Dequeue your cell
let cell = self.tableView.dequeueReusableCell(withIdentifier: "YourCustomCellIdentifier") as! YourCustomTableViewCell
// get picture url from the data array
let pictureUrl = self.yourCellsData[indexPath.row].pictureUrl
// async download
cell.pictureView.af_setImage(
withURL: URL(string: pictureUrl)!,
placeholderImage: UIImage(named: "YourPlaceholder.png"),
filter: nil,
imageTransition: UIImageView.ImageTransition.crossDissolve(0.5),
runImageTransitionIfCached: false) {
// Completion closure
response in
// Check if the image isn't already cached
if response.response != nil {
// Force the cell update
self.tableView.beginUpdates()
self.tableView.endUpdates()
}
}
return cell
}
That's all folks! ;-)