27

我正在开发一个应用程序,其中有一个 UITableViewCell 的自定义子类。我想根据其中的文本使单元格的高度动态化。我尝试在我的 heightForRowAtIndexPath 方法中这样做。但是我遇到了一些问题,以下代码导致和 EXC_BAD_ACCESS(code=2 address=0xb7ffffcc) 错误。

- (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;
}

如果我有:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{   
    return 100;
}

有用。

它似乎在这条线上崩溃了:PostCell *cell = (PostCell *)[tableView cellForRowAtIndexPath:indexPath];,我不知道为什么。我试图构建干净并重置模拟器,但都没有奏效。任何想法为什么会发生这种情况?

4

5 回答 5

54

我试图重现这个问题。事实证明,cellForRowAtIndexPath:内部调用heightForRowAtIndexPath会导致heightForRowAtIndexPath递归调用。这是 3 个递归步骤后堆栈回溯的摘录:

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

最后程序崩溃了。在我的模拟器上,当回溯跟踪深度约为 57000 级时,就会发生这种情况。


旧答案(没有错,但没有解释 EXC_BAD_ACCESS):

问题是

[tableView cellForRowAtIndexPath:indexPath]

返回nil当前不可见的行。表格视图只分配显示当前可见行所需的这么多单元格。当您滚动表格视图时,单元格会被重用。

但是在显示任何行之前heightForRowAtIndexPath为表格视图的所有单元格调用。

因此,您不应该从表格视图单元格中获取文本来计算heightForRowAtIndexPath. 您应该改为从数据源中获取文本。

于 2012-09-29T13:42:46.280 回答
7

tableView heightForRowAtIndexPath调用 before cellForRowAtIndexPath,在显示单元格之前,需要先计算高度。

你应该text从你的数据源获取,而不是从cell

于 2012-09-29T15:26:33.260 回答
2

我遇到了类似的问题并遇到了,只是为了分享我现在所做的对我有用的事情

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
}

希望这可以帮助任何寻找这个和像我一样的 iOS 编程新手:-)

于 2014-03-28T12:40:31.213 回答
0

在这种情况下,您应该调用 UITableViewDataSource 方法来获取 indexPath 的单元格,而不是 UITableView 的 cellForRowAtIndexPath: 方法,因为在第一次调用 tableView: heightForRowAtIndexPath: 时,tableView 中没有任何单元格。

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath: (NSIndexPath *)indexPath {
    PostCell *cell = (PostCell *)[self tableView: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;
}

它可以工作并且不会导致 EXC_BAD_ACCESS 异常。

于 2015-04-13T06:51:38.413 回答
-1

迅速:

使用它来了解表何时加载:

extension UITableView {
    func reloadData(completion: ()->()) {
        UIView.animateWithDuration(0, animations: { self.reloadData() })
            { _ in completion() }
    }
}

创建一个布尔值

var tables_loaded = Bool(false)

然后在 viewDidLoad 中:

tableView.reloadData {
    tables_loaded = true
    // call functions or other stuff regarding table manipulation.
    // tableView.beginUpdates()
    // tableView.endUpdates()
}

然后在

override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
            if tables_loaded {
                let cell = tableView.cellForRowAtIndexPath(indexPath)
                 // Do your magic here!
             }

}
于 2015-04-19T16:53:36.233 回答