0

I have a very similar question as posted here (Dynamic UIImageView Size Within UITableView) where I'm trying to dynamically retrieve an image from Firebase and make the resulting tableview adjust to the height of the image given a fixed width across the screen based on the aspect ratio. All the articles I read says to make the cell calculation based on cellforRowAt, but my actual image is within the TableViewCell. Can someone please help?

Tableview controller:

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "FeedTableViewCell", for: indexPath) as! FeedTableViewCell

    cell.configureCell(post: postArray[indexPath.row])

    return cell
}

TableViewCell:

func configureCell(post: Post){

   self.postImage.sd_setImage(with: URL(string: post.postImageURL), 
    placeholderImage: UIImage(named: "default"))
}
Mr.Kushwaha
  • 491
  • 5
  • 14
John Wong
  • 25
  • 8
  • use Disptach.main.asyn{cell. postImage.kf.setImage(with: URL) { _, _, _, _ in cell.layoutIfNeeded() } } – AyAz May 11 '18 at 06:19

2 Answers2

0

you have cell.setNeedsLayout() inside the completion handler of setting image method, like this:

cell. postImage.kf.setImage(with: URL) { _, _, _, _ in
    cell.layoutIfNeeded()
}
Abdelahad Darwish
  • 5,969
  • 1
  • 17
  • 35
0

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()
    }
}
Milan Nosáľ
  • 19,169
  • 4
  • 55
  • 90
  • Does it matter that my self.postImage.sd_setImage(with: URL(string: post.postImageURL) is currently located in my tableviewcell file, not tableviewcontroller? – John Wong May 26 '18 at 02:48