Nick Allen
Nick Allen

Reputation: 1873

How to update self-sizing tableview's height?

If I put a custom view in a cell , how can I notify tableview to update cell's height if my custom view's height has changed? I tried invalidateIntrinsicContentSize but that doesn't work. ReloadData works but I doubt whether using

self.superview?.superview?.superview as! UITableView).reloadData()

is a good implementation. I have seen a similar question here but that's all about view from standard library. And this and this have no answer.

Upvotes: 0

Views: 335

Answers (1)

joern
joern

Reputation: 27620

You are thinking in the right direction. However there are two problems with your approach:

  1. reloadData reloads the whole table view and it does not animate the change.
  2. Moving up the superview chain is bound to break when Apple changes the UITableView view hierarchy. They have done that before, so they might do it again.

To fix the first issue you should call reloadRowsAtIndexPaths:withRowAnimation: instead. That only reloads the cells that you specify in the indexPath array. So you pass it an array that only contains the indexPath of your cell.

The second issue is a bit trickier because a UITableViewCell has no reference to its UITableView (and it shouldn't). So it cannot tell the UITableView directly to reload the cell.

You can give each cell a closure that it should execute whenever its height has changed. So you just add a closure property in your custom cell:

class YourCustomTableViewCell: UITableViewCell {
    var resizeClosure: (() -> Void)?
    ...

And you set this closure in your UITableViewControllerDataSource when you dequeue the cell:

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell:UITableViewCell = tableView.dequeueReusableCellWithIdentifier("YourCustomCellIdentifier", forIndexPath: indexPath)
        if let customCell = cell as? YourCustomTableViewCell {
            customCell.resizeClosure = { [weak cell, weak tableView] in
                if let currentIndexPath = tableView?.indexPathForCell(cell!) {
                    tableView?.reloadRowsAtIndexPaths([currentIndexPath], withRowAnimation: .Automatic)
                }
            }
        }

        return cell
    }

Just make sure that you add tableView to the closure's capture list to avoid a strong reference cycle. That is done by adding the [weak tableView] to the closure.

And then when the cell changes its height you just execute the closure and the cell will be reloaded:

func someFunctionThatChangesTheHeight() {
   // change the height
   resizeClosure?()
}

Upvotes: 1

Related Questions