Custom UITableViewCell with auto layout and accessory view
Asked Answered
P

2

6

I have a custom table view cell that uses auto layout and has a disclosure indicator as an accessory view. The cell size of the cells on the screen are completely wrong when first displayed:

As you can see the cell is taking about a 1.5 screens worth of space:

enter image description here

However if I rotate the device and rotate back it looks fine:

enter image description here

As you can see here, I've done nothing complicated:

enter image description here

I have a very NON-IDEAL workaround which is to do:

-(void)viewDidAppear:(BOOL)animated 
{
    [super viewDidAppear:animated];
    [self.tableView reloadData];
}

But that obviously causes a 'flash' when you first see the screen. In a more complicated scenario the flash is far more obvious.

I have another workaround but this causes an auto layout exception:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 
{
    BasicCell *cell = [tableView dequeueReusableCellWithIdentifier:@"BasicCell" forIndexPath:indexPath];
    cell.basicLabel.text = @"Hello this is just some text that should get the label to go over multiple lines";
    [cell.basicLabel layoutIfNeeded];
    return cell;
}

Exception:

enter image description here

At least this method doesn't give me UI flashing.

If I remove the accessory view it actually works perfectly fine.

UPDATE: I've added a sample project to github: https://github.com/fwaddle/TableCellAccessoryTest

UPDATE #2: Turns out another work around this bug is to layout the cell in code. I just tried doing the same thing in code and it didn't throw the warning and worked fine. Looks like an IB bug.

Any ideas how to work around this issue? Thanks.

Photostat answered 8/5, 2015 at 2:42 Comment(8)
Set your constraints properly inside of your cell, which can be tricky sometimes, then set the rowHeight property on your table view to UITableViewAutomaticDimension. Also that is not an exception, that is a warning do to misconfiguration. An uncaught exception will terminate your application.Lexicography
Hi @Nikita, The constraints are set properly and the i have set the rowHeight properly: - (void)viewDidLoad { [super viewDidLoad]; self.tableView.rowHeight = UITableViewAutomaticDimension; self.tableView.estimatedRowHeight = 45; } But I take your point about it not being an exception.Photostat
I think this is an issue with your constraints. Clean your build and remove constraints that are not being used in the layout, then build & run it.Lexicography
There are only 4 constraints to stick the label to all 4 sides. There are no unrequired constraints that I can see. In the above screen shot you can see the 4 constraints.Photostat
Try increasing the estimatedRowHeight, I had this problem and when I increased that value it worked perfectly.Lexicography
Increasing the estimatedRowheight didn't have an effect. I've uploaded the sample project to github and updated the above with the link.Photostat
You should file a bug report with Apple. Adding a disclosure indicator certainly shouldn't break self-sizing cells.Opening
Hi @Opening I just filed a bug report with Apple. I agree that this certainly looks like a bug in iOS. Thanks.Photostat
P
0

So even creating the constraints in code sometimes doesn't fix this issue. It seems you need a few more changes. In your custom table cell add the following especially if you're changing the accessory type depending on the contents of the cell (e.g. Checkmark):

-(void) setAccessoryType:(UITableViewCellAccessoryType)accessoryType {
  [super setAccessoryType:accessoryType];
  [self setNeedsUpdateConstraints];
}

Also remove the prototype cell in the storyboard and register your class instead:

-(void) viewDidLoad {
  [super viewDidLoad];
  [self.tableView registerClass:[MyCustomCell class] forCellReuseIdentifier:@"MyCustomCell"];
}

I occasionally find that I still need to force labels (especially multi-line labels it seems) to re layout in cellForRowAtIndexPath:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
  MyCustomCell *cell = [tableView dequeueReusableCellWithIdentifier:@"MyCustomCell" forIndexPath:indexPath];
  cell.customLabel.text = .....
  [cell.customLabel layoutIfNeeded];
  return cell;
}

All of the above fixed my issues so I hope they're of use to others and you don't waste a large amount of time on this.

I still haven't heard anything back from Apple about the bug report but I assume that's pretty normal.

Photostat answered 6/8, 2015 at 21:33 Comment(0)
L
3

Implement the following delegate method as this solved the problem for me.

- (void)tableView:(UITableView *)tableView   
willDisplayCell:(UITableViewCell *)cell 
forRowAtIndexPath:(NSIndexPath*)indexPath

A table view sends this message to its delegate just before it uses cell to draw a row, thereby permitting the delegate to customize the cell object before it is displayed. This method gives the delegate a chance to override state-based properties set earlier by the table view, such as selection and background color. After the delegate returns, the table view sets only the alpha and frame properties, and then only when animating rows as they slide in or out.

Add this code to your tableViewController:

 - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell   forRowAtIndexPath:(NSIndexPath *)indexPath{

    BasicCell *basicCell = (BasicCell *)cell;
    basicCell.basicLabel.text = @"Hello this is just some text that should get the label to go over multiple lines";

}
Lexicography answered 8/5, 2015 at 3:53 Comment(1)
Although your solution is the best work around so far, it is still not perfect. Your solution still produces cells that are too high. Have a look what happens when you rotate the device clockwise and then back. You'll notice the cells get smaller and infant to the correct height. I've also filed a bug report with Apple as this looks like an iOS bug.Photostat
P
0

So even creating the constraints in code sometimes doesn't fix this issue. It seems you need a few more changes. In your custom table cell add the following especially if you're changing the accessory type depending on the contents of the cell (e.g. Checkmark):

-(void) setAccessoryType:(UITableViewCellAccessoryType)accessoryType {
  [super setAccessoryType:accessoryType];
  [self setNeedsUpdateConstraints];
}

Also remove the prototype cell in the storyboard and register your class instead:

-(void) viewDidLoad {
  [super viewDidLoad];
  [self.tableView registerClass:[MyCustomCell class] forCellReuseIdentifier:@"MyCustomCell"];
}

I occasionally find that I still need to force labels (especially multi-line labels it seems) to re layout in cellForRowAtIndexPath:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
  MyCustomCell *cell = [tableView dequeueReusableCellWithIdentifier:@"MyCustomCell" forIndexPath:indexPath];
  cell.customLabel.text = .....
  [cell.customLabel layoutIfNeeded];
  return cell;
}

All of the above fixed my issues so I hope they're of use to others and you don't waste a large amount of time on this.

I still haven't heard anything back from Apple about the bug report but I assume that's pretty normal.

Photostat answered 6/8, 2015 at 21:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.