1

tl;dr How can I get correct the height of my tableView after cell height changes made due to cell expansion——without using delays?

My tableViewcells, expand and collapse. Because of that I need to pass (using a delegate method) the tableView's height to it's superView. It's superview is a collectionviewcell.

extension ArtistDetailViewController: UITableViewDelegate{

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

        guard let cell = tableView.cellForRow(at: indexPath) as? WorkTableViewCell else {
            fatalError()
        }

        //1: create model
        var work = works[indexPath.row]

        //2: update the model
        work.isExpanded = !work.isExpanded

        //3: upadte dataSource
        works[indexPath.row] = work

        //4: update the text of textView
        cell.moreInfoTextView.text = work.isExpanded ? work.info : WorkTableViewCell.textViewText
        cell.moreInfoTextView.textAlignment = work.isExpanded ? .left : .center

        //5: update TableView
        tableView.beginUpdates()
        tableView.endUpdates()
        tableView.scrollToRow(at: indexPath, at: .top, animated: true)

        //6: slavishly update cell with everything you got!
        cell.setNeedsUpdateConstraints()
        cell.updateConstraintsIfNeeded()

        cell.contentView.setNeedsUpdateConstraints()
        cell.contentView.updateConstraintsIfNeeded()
        cell.contentView.setNeedsLayout()
        cell.contentView.layoutIfNeeded()

        print(tableView.contentSize.height) // prints 252
        //7: pass tableView height with delay
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.01, execute: {
            print(tableView.contentSize.height) // prints 341
            self.tableViewHeight = tableView.contentSize.height
            self.delegate?.didRender(with: self.tableViewHeight)
        })
    }
}

my ArtistDetailViewController has a tableView property.

The tableView property subclassed from a tableView which has intrinsicSize.

class AutomaticHeightTableView: UITableView {

  override var intrinsicContentSize: CGSize {
    return contentSize
  }

  override func reloadData() {
    super.reloadData()
    invalidateIntrinsicContentSize()
  }
}

The problem I have is if I don't use any delay (step 7) is:

  1. The tableView's height would be enlarged enough ie it would enlarge, but just not enlarge enough.
  2. The tableView won't collapse later.
  3. Even if I do use delay sometimes it won't collapse/expand \o/

Obviously the problem is that these updates are done in an async manner and the the value is calculated too quick! As for proof:

If you saw the prints, The height of the tableView changes from 252 to 341—after the delay.

In my step 6 I tried adding all sorts of updates so that I could avoid adding a delay but that didn't work.

The result with delay:

enter image description here

The result without delay:

enter image description here

mfaani
  • 33,269
  • 19
  • 164
  • 293
  • Are you trying to show anything other than one image, a "sub title", and an expanding/collapsing paragraph? – DonMag Dec 05 '17 at 19:45
  • @DonMag yes! *This* is just a water downed/dummy representation of getting two actual tableViewcontrollers into the 1st and 2nd cell of a collectionviewcontroller. – mfaani Dec 05 '17 at 19:47
  • 1
    OK - table views are designed for scrolling repeating rows of data, which is why there's no "pre-defined" way to do what you want. I'd suggest re-thinking your approach, but if you *really* want to do it that way, look into using KVO to Observe the tableview's contentSize change. – DonMag Dec 05 '17 at 19:52
  • @DonMag so if I change the way that I'm **animating** ie remove begin/update, then row resizing suddenly work.e.g. if I do `tableView.reloadData` or do `tableView.reloadData` inside an animation block then it works. Same for `tableView.reloadRows(at: [indexPath], with: .fade)`. They all work. Having that said, Ive barely used KVO, I would like to give it a try, good thinking. One question: say if the tableview's height was to animate and change from 250-300 would KVO give 50 callbacks?! That won't be good for me, because per a callback I reload the collectionView and I don't want 50 callbacks. – mfaani Dec 05 '17 at 20:14
  • If you are using auto-layout and constraints to create "auto-sizing" cells, you do not need to call reloadData() ... When you change the content, you can simply call `tableView.beginUpdates()` followed by `tableView.endUpdates()` and auto-layout will re-caclulate the cells and automatically animate the change. – DonMag Dec 05 '17 at 20:42
  • @DonMag I think u misread my last comment. If I don't use beginUpdates/endUpdates and user other ways or reloading...then I won't have any problem and the correct height is passed – mfaani Dec 05 '17 at 20:50
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/160543/discussion-between-honey-and-donmag). – mfaani Dec 05 '17 at 21:06

0 Answers0