Подтвердить что ты не робот

EXC_BAD_ACCESS в heightForRowAtIndexPath iOS

Я работаю над приложением, где у меня есть пользовательский подкласс 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];, и я не знаю, почему. Я попытался создать чистый и reset симулятор, но не работал. Есть идеи, почему это происходит?

4b9b3361

Ответ 1

Я попытался воспроизвести проблему. Оказывается, вызов cellForRowAtIndexPath: внутри heightForRowAtIndexPath вызывает heightForRowAtIndexPath для рекурсивного вызова. Ниже приведен фрагмент стоп-кадра после трех шагов рекурсии:

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. Вместо этого вы должны получить текст из своего источника данных.

Ответ 2

tableView heightForRowAtIndexPath вызывается перед cellForRowAtIndexPath перед тем, как будет отображаться ячейка, сначала нужно вычислить высоту.

вы должны получить text из своего источника данных, а не из cell

Ответ 3

У меня была аналогичная проблема и наткнулся на нее, просто чтобы поделиться тем, что я делаю сейчас, что хорошо работает для меня

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, как я: -)

Ответ 4

В этом случае вы должны вызвать метод UITableViewDataSource для получения ячейки для indexPath вместо метода cellForRowAtIndexPath: метода UITableView, поскольку в тот момент, когда tableView: heightForRowAtIndexPath: в первый раз, в таблицеView нет никаких ячеек.

- (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.

Ответ 5

СВИФТ:

Используйте это, чтобы знать, когда загружены таблицы:

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!
             }

}