Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iOS 8 UITableView first row has wrong height

I'm working on an app where I face a strange issue. I've created a UITableViewController in the storyboard and added a prototype cell. In this cell, I've added an UILabel element and this UILabel takes up the whole cell. I've set it up with Auto Layout and added left, right, top and bottom constraints. The UILabel contains some text.

Now in my code, I initialize the the rowHeight and estimatedRowHeight of the table view:

override func viewDidLoad() {
    super.viewDidLoad()

    self.tableView.rowHeight = UITableViewAutomaticDimension
    self.tableView.estimatedRowHeight = 50
}

And I create the cell as follows:

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    var cell : UITableViewCell? = tableView.dequeueReusableCellWithIdentifier("HelpCell") as? UITableViewCell
    if(cell == nil) {
        cell = UITableViewCell(style: .Default, reuseIdentifier: "HelpCell")
    }
    return cell!
}

I return two rows in my table view. Here comes my problem: the height of the first row is way to large. It appear that the second, third row etc all have a correct height. I really don't understand why this is the case. Can someone help me with this?

like image 932
Devos50 Avatar asked Dec 03 '14 19:12

Devos50


2 Answers

I had a problem where the cells' height were not correct on the first load, but after scrolling up-and-down the cells' height were fixed.

I tried all of the different 'fixes' for this problem and then eventually found that calling these functions after initially calling self.tableView.reloadData.

            self.tableView.reloadData()
            // Bug in 8.0+ where need to call the following three methods in order to get the tableView to correctly size the tableViewCells on the initial load.
            self.tableView.setNeedsLayout()
            self.tableView.layoutIfNeeded()
            self.tableView.reloadData()

Only do these extra layout calls after the initial load.

I found this very helpful information here: https://github.com/smileyborg/TableViewCellWithAutoLayoutiOS8/issues/10

Update: Sometimes you might have to also completely configure your cell in heightForRowAtIndexPath and then return the calculated cell height. Check out this link for a good example of that, http://www.raywenderlich.com/73602/dynamic-table-view-cell-height-auto-layout , specifically the part on heightForRowAtIndexPath.

Update 2: I've also found it VERY beneficial to override estimatedHeightForRowAtIndexPath and supply somewhat accurate row height estimates. This is very helpful if you have a UITableView with cells that can be all kinds of different heights.

Here's a contrived sample implementation of estimatedHeightForRowAtIndexPath:

public override func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {

    let cell = tableView.cellForRowAtIndexPath(indexPath) as! MyCell

    switch cell.type {
    case .Small:
        return kSmallHeight
    case .Medium:
        return kMediumHeight
    case .Large:
        return kLargeHeight
    default:
        break
    }
    return UITableViewAutomaticDimension
}

Update 3: UITableViewAutomaticDimension has been fixed for iOS 9 (woo-hoo!). So you're cells should automatically size themselves without having to calculate the cells height manually.

like image 161
Sakiboy Avatar answered Nov 15 '22 20:11

Sakiboy


As the Apple says in the description of setNeedsLayout:

This method does not force an immediate update, but instead waits for the next update cycle, you can use it to invalidate the layout of multiple views before any of those views are updated. This behavior allows you to consolidate all of your layout updates to one update cycle, which is usually better for performance.

Because of this you should add needed lines of code (which should be executed with right layout) in dispatch_after block(which will put your method in queue of RunLoop). And your code will be executed after needs layout applies.

Example:

- (void)someMethod {

[self.tableView reloadData];

[self.tableView setNeedsLayout];

[self.tableView layoutIfNeeded];

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

            //code which should be executed with the right size of table

        });
like image 20
Alexey Pelekh Avatar answered Nov 15 '22 21:11

Alexey Pelekh