First of all, you need to use Autolayout to calculate the proper cell height by creating a proper set of constraints that would determine the cell height based on content. I am going to assume you did that.
Then you have to tell the tableView you want it to use Autolayout to calculate height:
// my best estimation of the height
tableView.estimatedRowHeight = 144
// but the real height is calculated by autolayout
tableView.rowHeight = UITableViewAutomaticDimension
Now this would work if the autolayout could calculate the height correctly in cellForRowAt. But since the image is downloaded asynchronously, the image is set later, when the cell may be already presented. This requires you to provide a way in which a cell can tell the tableView that it has downloaded its content and its layout needs to be recalculated. To do so, use this method in the viewController with the tableView:
func recalculateTableViewLayout() {
self.tableView.beginUpdates()
self.tableView.setNeedsLayout()
self.tableView.endUpdates()
}
You will need to pass the reference to the viewController with the tableView to each cell (I recommend to use delegate pattern for that, here for brevity I will simply sketch it using it directly):
class FeedTableViewCell: UITableViewCell {
weak var feedViewController: FeedViewController?
// etc.
And in the cellForRowAt:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "FeedTableViewCell", for: indexPath) as! FeedTableViewCell
cell.feedViewController = self
cell.configureCell(post: postArray[indexPath.row])
return cell
}
Then use completion handler of sd_setImage to tell the tableView to recalculate its layout when the image gets downloaded:
func configureCell(post: Post){
self.postImage.sd_setImage(with: URL(string: post.postImageURL), placeholderImage: UIImage(named: "default")) { (image, error, cache, url) in
self.feedViewController?.recalculateTableViewLayout()
}
}