I'm working on an application where I have a custom subclass of UITableViewCell. I want to make the cell's height dynamic based on the text inside it. I try do do that in my heightForRowAtIndexPath method. But I'm having some issues, the following code causes and EXC_BAD_ACCESS(code=2 address=0xb7ffffcc) error.
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
PostCell *cell = (PostCell *)[tableView cellForRowAtIndexPath:indexPath];
CGSize postTextSize;
if (![cell.postText.text isEqualToString:@""]) {
postTextSize = [cell.postText.text sizeWithFont:[UIFont systemFontOfSize:14] constrainedToSize:CGSizeMake(cell.postText.frame.size.width, 200)];
} else {
postTextSize = CGSizeMake(cell.postText.frame.size.width, 40);
}
return postTextSize.height + cell.userName.frame.size.height + 10;
}
If I instead have:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 100;
}
It works.
It seems to be crashing on this line: PostCell *cell = (PostCell *)[tableView cellForRowAtIndexPath:indexPath];
, and I don't know why. I have tried to build clean and reset the simulator, but neither worked. Any ideas why this is happening?
I have tried to reproduce the problem. It turns out that calling cellForRowAtIndexPath:
inside heightForRowAtIndexPath
causes heightForRowAtIndexPath
to be called recursively. Here is an extract of the stack backtrace after the 3 recursion steps:
frame #0: 0x000042d0 DocInteraction`-[DITableViewController tableView:heightForRowAtIndexPath:] + 48 at DITableViewController.m:262
frame #1: 0x0054f688 UIKit`-[UISectionRowData refreshWithSection:tableView:tableViewRowData:] + 3437
frame #2: 0x0055040f UIKit`-[UITableViewRowData(UITableViewRowDataPrivate) _ensureSectionOffsetIsValidForSection:] + 144
frame #3: 0x00551889 UIKit`-[UITableViewRowData numberOfRows] + 137
frame #4: 0x00553dac UIKit`-[UITableViewRowData globalRowsInRect:] + 42
frame #5: 0x003f82eb UIKit`-[UITableView(_UITableViewPrivate) _visibleGlobalRowsInRect:] + 177
frame #6: 0x004001e6 UIKit`-[UITableView cellForRowAtIndexPath:] + 113
frame #7: 0x000042f2 DocInteraction`-[DITableViewController tableView:heightForRowAtIndexPath:] + 82 at DITableViewController.m:262
frame #8: 0x0054f688 UIKit`-[UISectionRowData refreshWithSection:tableView:tableViewRowData:] + 3437
frame #9: 0x0055040f UIKit`-[UITableViewRowData(UITableViewRowDataPrivate) _ensureSectionOffsetIsValidForSection:] + 144
frame #10: 0x00551889 UIKit`-[UITableViewRowData numberOfRows] + 137
frame #11: 0x00553dac UIKit`-[UITableViewRowData globalRowsInRect:] + 42
frame #12: 0x003f82eb UIKit`-[UITableView(_UITableViewPrivate) _visibleGlobalRowsInRect:] + 177
frame #13: 0x004001e6 UIKit`-[UITableView cellForRowAtIndexPath:] + 113
frame #14: 0x000042f2 DocInteraction`-[DITableViewController tableView:heightForRowAtIndexPath:] + 82 at DITableViewController.m:262
frame #15: 0x0054f688 UIKit`-[UISectionRowData refreshWithSection:tableView:tableViewRowData:] + 3437
frame #16: 0x0055040f UIKit`-[UITableViewRowData(UITableViewRowDataPrivate) _ensureSectionOffsetIsValidForSection:] + 144
frame #17: 0x00551889 UIKit`-[UITableViewRowData numberOfRows] + 137
frame #18: 0x003ff66d UIKit`-[UITableView noteNumberOfRowsChanged] + 119
frame #19: 0x003ff167 UIKit`-[UITableView reloadData] + 764
Finally the program crashes. On my Simulator this happens when the back trace is about 57000 levels deep.
Old answer (not wrong, but does not explain the EXC_BAD_ACCESS):
The problem is that
[tableView cellForRowAtIndexPath:indexPath]
returns nil
for rows that are currently not visible. A table view allocates only so many cells that are required to display the currently visible rows. The cells are reused when you scroll the table view.
But heightForRowAtIndexPath
is called for all cells of the table view before any row is displayed.
As a consequence, you should not get the text from the table view cells to compute the height in heightForRowAtIndexPath
. You should get the text from your data source instead.
tableView heightForRowAtIndexPath
is called before cellForRowAtIndexPath
, before a cell is displayed, the height needs to be calculated first.
you should get text
from your data source, not from cell
I had a similar problem and came across, just to share what I do now which works well for me
CGFloat mycell_height;
- (void)viewDidLoad
{
[super viewDidLoad];
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:@"MyCell"];
mycell_height = cell.frame.size.height;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return mycell_height; //assuming all cells are the same
}
Hope this helps anyone looking for this and new to iOS programming like me :-)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With