7

Currently I am messing around with swift and dynamic table cell heights. I developed a simple app for iOS8.1 on xcode6.1: https://github.com/ArtworkAD/DynamicCellTest

So to achieve a cell height that stretches with the cell's content I do the following:

  • in storyboard set label lines to 0
  • set labels font to system
  • set constraints for label in cell
  • add self.tableView.rowHeight = UITableViewAutomaticDimension
  • don't override heightForRowAtIndex method

Minimal code is needed:

class MyTableViewController: UITableViewController {

    var entries:Array<String> = [String]()

    override func viewDidLoad() {
        super.viewDidLoad()

        //create dummy content
        var i = 0
        while i < 10 {
            entries.append("\(i) Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor")
            entries.append("\(i+1) Lorem ipsum dolor sit amet")
            i = i + 2;
        }

        self.tableView.rowHeight = UITableViewAutomaticDimension

    }

    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int)  -> Int {
        return self.entries.count
    }

    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

        let cell = self.tableView.dequeueReusableCellWithIdentifier("basic_cell", forIndexPath: indexPath) as UITableViewCell

        var label = cell.viewWithTag(13)

        if let unwrappedLabel = label as? UILabel {
            unwrappedLabel.text = self.entries[indexPath.row]
        }

        return cell
    }
}

The left image shows the result of the above code. The cell height grows with the content of the label, all nice. However when you click on the disclosure indicator to the detail view and move back again, you get the right image. Why is this happening??

enter image description here

A bad solution for this problem is to override this methods:

override func viewWillDisappear(animated: Bool) {
    super.viewWillDisappear(animated)
    self.tableView.reloadData()
}

override func viewDidAppear(animated: Bool) {
    super.viewDidAppear(animated)
    self.tableView.reloadData()
}

override func viewDidDisappear(animated: Bool) {
    super.viewDidDisappear(animated)
    self.tableView.reloadData()
}

This way the above problem is solved, but this solution seems not right? A side effect of it is, that when self.tableView.reloadData() is called the table view port jumps to the first cell which doesn't look nice.

Does anyone has an idea what I am doing wrong? Feel free to clone my repo https://github.com/ArtworkAD/DynamicCellTest and test it out.

DarkLeafyGreen
  • 69,338
  • 131
  • 383
  • 601

5 Answers5

4

Adding this seems that it is able to fix rotation problem.

override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
    self.tableView.estimatedRowHeight = 36.5
}

However, I have another case that there are 4 labels in the cell, which has the same rotation problem, adding this is not enough and I ended up replacing the self.tableView.estimatedRowHeight = 36.5 with reloading visible cells.

gabbler
  • 13,626
  • 4
  • 32
  • 44
1

I've just solved exactly that problem by overriding tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) in UITableViewDelegate object.

func tableView(tableView: UITableView, 
               estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) 
               -> CGFloat {
    return 200
}

Changing the exact returned value apparently have no effect. The cells are always properly self-sizing. I've got no idea why providing estimated height causes the autolayout to kick in, but it does. iOS 8.1.2, Xcode 6.1.

siejkowski
  • 1,621
  • 12
  • 11
0

The link provides a good explanation of what needs to be done.

Using Auto Layout in UITableView for dynamic cell layouts & variable row heights

For your specific problem you are missing two lines of code.

In viewDidLoad under your tableView.rowHeight add self.tableView.estimatedRowHeight = 64

The above will provides allow the tableView to assign estimated height values for offscreen cells to enhance performance and it also calculates the scroll bar height.

The main problem is you are missing unwrappedLabel.preferredMaxLayoutWidth = CGRectGetWidth(tableView.bounds) in cellForRowAtIndexPath this tells the label its preferred maximum width so it can calculate the height it requires for multi-line text. Usually you would do this in the table view cell subclass.

I have tested this in your project and it works

Community
  • 1
  • 1
JLau_cy
  • 705
  • 1
  • 7
  • 14
  • I spent 3 days reading such articles :) I would suggest you checkout my code if you have time. – DarkLeafyGreen Nov 06 '14 at 11:02
  • Sorry I can't do it right now because I'm on a training course. I have done this before so I have a solution, i will do the coding once I am back on my laptop. :) – JLau_cy Nov 06 '14 at 11:05
  • Setting `estimatedRowHeight` to any value fixed the issue that the cell heights are destroyed on push - still an issue remains: when pushing, the vertical position of the tableview is altered, and the selected cell jumps out of the view. – fabb Nov 22 '14 at 11:26
0

You have to set estimatedRowHeight:

// setup automatic row height
self.tableView.rowHeight = UITableViewAutomaticDimension

// whatever you think is the approximate row height
self.tableView.estimatedRowHeight = 44
pronebird
  • 12,068
  • 5
  • 54
  • 82
-2

Also came across an alternative here http://useyourloaf.com/blog/2014/08/07/self-sizing-table-view-cells.html. A comment mentions -

Simply make the cell layout its subviews before returning it from cellForRowAtIndexPath:

[cell setNeedsDisplay];
[cell layoutIfNeeded];

Check this

Aniket Bochare
  • 427
  • 2
  • 10