dequeued UITableViewCell has incorrect layout until scroll (using autolayout)

10,135

Solution 1

If you name a property on a UITableViewCell subclass textLabel or defaultTextLabel, then IB will ignore the constraints you have specified and override them with default ones, with no warnings issued.

This is the case even on cells designed in IB with the Custom style, which have no visible textLabel or detailTextLabel properties.

This also happen if add a property of type UIImageView property on a UITableViewCell subclass and name it imageView.

Solution 2

In accordance to this multiple lines UILabel GitHub issue, this is a lingering iOS bug.

I found that in iOS 9+, this situation mostly occurs in editing mode, with much unpredictability.

The following workaround only partially works: it requires redrawing the UITableView twice, and still does not cover all scenarios.

override func viewDidLoad() {
    super.viewDidLoad()

    tableView.setNeedsLayout()
    tableView.layoutIfNeeded()
    tableView.reloadData()
}

Notes:

  • Using UITextView is a great alternative to multiple line UILabel, without the bug. UITextView does not exhibit any of the IULabel other oddities either, like alignment errors or flickering.
  • There is also an alternative solution on SO-25947146, which did not work for me but is worth mentioning.
  • Seems to occur prominently when self.tableView.editing is true
  • Using low values for tableView.estimatedRowHeight reduces the occurrence
  • Demonstration of the bug on SwiftArchitect/TableViewControllerRowHeightBug gist
Share:
10,135

Related videos on Youtube

sapi
Author by

sapi

Updated on June 19, 2022

Comments

  • sapi
    sapi almost 2 years

    I have a custom UITableViewCell subclass which has had autolayout constraints applied to it in Interface Builder. The cell contains multiple views, including a UITextField.

    Relevantly, the size of the UITextField is constrained such that there is default horizontal spacing between it and the next view.

    The cell is instantiated like follows:

    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        static NSString *CellIdentifier = @"ProgressCell";
        ProgressCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier
                                                         forIndexPath:indexPath]
    
        cell.textField.text = @"Some string that is different for each cell";
    
        return cell;
    }
    

    When the cell first appears, the UITextField overruns the correct frame, and appears behind the UIView to its right. However, when I scroll the cell off screen, pause, and then scroll back, the text is truncated correctly.

    An example is shown below (at the second edit).

    I have tried calling [cell setNeedsLayout] and [cell setNeedsDisplay] for the cell in cellForRowAtIndexPath, as well as performing them after a delay. Neither is effective.

    What is scrolling off screen doing that is causing the cell to appear correctly, and how can I either replicate this or fix the underlying issue?

    EDIT:

    Calling

    [self.tableView reloadData];
    [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationAutomatic];
    

    in order to reload the cell, appears to cause the layout to appear correctly first time.

    However, it now breaks (occasionally) on scrolling (ie, when scrolling back up, the layout constraints are now not applied correctly).

    Calling [cell setNeedsLayout] in cellForRowAtIndexPath appears not to fix this issue.

    EDIT2:

    The top cell, as shown here, appeared correctly (as the bottom cell does) until I scrolled down the screen. It since disappeared.

    This reflects the problem as of the first edit - it's the second rendering that is the problem (makes me think that it might have something to do with reusing the cell?)

    • rdelmar
      rdelmar about 11 years
      Are you getting any warnings about the constraints in the console?
    • jrturton
      jrturton about 11 years
      Can you include some screenshots?
    • jrturton
      jrturton about 11 years
      @rdelmar I wrote up my Autolayout category (didnt we discuss that earlier?) if you're interested: commandshift.co.uk/blog/2013/02/20/…
    • sapi
      sapi about 11 years
      I've uploaded an example image here: imgur.com/a/IKv0Z. There are no constraint warnings in the console @rdelmar - and they're all set up through IB, so they should be valid.
    • rdelmar
      rdelmar about 11 years
      I don't see anything wrong with those constraints. I tried to duplicate your problem, but mine truncated long text without scrolling. If you make the width constraint = instead of >= does that help? Can you just delete that one (it will depend on what you have for the other 2 objects to the right)?
    • sapi
      sapi about 11 years
      I've tried making the width equal, deleting the constraint, and even ignoring autolayout altogether and setting the autoresizing masks. None of those ideas fixed the issue :/ Even with a fixed width, if I set the textLabel's background colour, I can see that it is taking up the entire cell.
  • RickiG
    RickiG over 10 years
    The same goes for the "imageView" property as well.
  • atreat
    atreat over 10 years
    This answer needs to be promoted into the UITableViewCell class reference, I just wasted 4 hours of my life. its 4 in the morning...
  • Ravi
    Ravi about 10 years
    Thank you so much. You just saved me several hours of my life.
  • race_carr
    race_carr about 10 years
    I wish Interface Builder or the compiler gave you a warning about inadvertently overriding these Property names.
  • Dylan Moore
    Dylan Moore about 9 years
    The same goes for UIImageView properties named "maskView" as well.
  • miken32
    miken32 over 2 years
    This approach was already suggested in a previous answer 5 years ago.
  • Đạt Trần
    Đạt Trần over 2 years
    @miken32 Because I have tried to write it on viewDidLoad and got no work so...