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

Как я могу правильно использовать ячейки таблицы высот таблицы на iPhone?

Мое приложение должно иметь ячейки таблицы с переменной высотой (как в каждой ячейке таблицы отличается по высоте, а не то, что каждая ячейка должна иметь возможность изменять ее размер).

У меня есть решение, которое в настоящее время работает, но оно kludgy и медленное.

Мое текущее решение:

Прежде чем отображать ячейки таблицы, я подсчитаю, насколько высокой должна быть каждая ячейка, вызывая методы определения размера, такие как -sizeWithFont:constrainedToSize: в своих данных. Затем я добавляю высоту, допускаю некоторое заполнение и сохраняю результат с данными.

Затем, когда мой UITableViewDelegate получает -tableview:heightForRowAtIndexPath:, я определяю, какой элемент будет отображаться для этой ячейки и вернуть высоту, которую я высчитал ранее.

Как я уже сказал, это работает, но вызов -sizeWithFont:constrainedToSize: очень медленный, когда вы делаете это для сотен элементов последовательно, и я чувствую, что это можно сделать лучше.

Итак, для этого я должен был поддерживать две части кода - одну, которая вычисляла высоты ячеек, и тот, который на самом деле рисовал бы ячейки, когда придет время.

Если что-либо изменилось в отношении элемента модели, мне пришлось обновить оба этих фрагмента кода, и теперь они все еще не идеально подходят друг к другу, иногда приводя к тому, что ячейки таблицы немного слишком малы для заданного элемента, или слишком большой.

Мое предложение:

Поэтому я хочу покончить с предварительным вычислением высоты ячейки. A), потому что он нарушает парадигму MVC и B), потому что она медленная.

Итак, моя ячейка рисует себя и, как результат, заканчивается правильной высотой ячейки. Моя проблема заключается в том, что я не могу сказать, что таблица просматривает высоту ячейки до ее нарисования - к тому времени ее слишком поздно.

Я попытался вызвать -cellForRowAtIndexPath: из -tableview:heightForRowAtIndexPath:, но это застряло в бесконечном цикле, так как первый вызывает второй в какой-то момент и наоборот (по крайней мере, это то, что я видел, когда я его пробовал).

Итак, этот вариант не может быть и речи.

Если я не укажу размер в высоте для метода делегата row, тогда представление таблицы будет скрупулезным. Ячейки являются идеальной высотой, но их положение x - это ячейки фиксированных высот.

Ячейки со смешанной таблицей http://jamsoftonline.com/images/messed_table_cells.png

Обратите внимание, как нижняя ячейка имеет правильный размер - она ​​просто перекрывает предыдущую ячейку, а предыдущая ячейка перекрывает ее предыдущую и т.д. и т.д.

Также, используя этот метод, при прокрутке есть некоторые артефакты, которые, как я думаю, могут быть связаны с идентификатором повторного использования для ячеек.

Поэтому любая помощь здесь была бы признательна.

4b9b3361

Ответ 1

Вот что я использую. У NSString есть метод, который расскажет вам размеры текстового поля на основе информации о шрифте и ограничениях высоты и ширины, которые вы ему даете.

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSString *text = [self getTextForIndexPath:indexPath];
    UIFont *font = [UIFont systemFontOfSize:14];
    CGSize size = [self getSizeOfText:text withFont:font];

    return (size.height + 11); // I put some padding on it.
}

Затем вы пишете метод вытягивания текста для этой ячейки...

- (NSString *)getTextForIndexPath:(NSIndexPath *)indexPath
{
    NSString *sectionHeader = [self.tableSections objectAtIndex:[indexPath section]];
    NSString *sectionContent = [self.tableData objectForKey:sectionHeader];

    return sectionContent;
}

И это нужно, чтобы получить размер текста.

- (CGSize)getSizeOfText:(NSString *)text withFont:(UIFont *)font
{
    return [text sizeWithFont:font constrainedToSize:CGSizeMake(280, 500)];
}

Ответ 2

Просто мысль:

  • Что, если бы у вас было, скажем, шесть разных типов ячеек, каждый со своим идентификатором и фиксированной высотой. Один из них будет для однострочной ячейки, другой для двухстрочной ячейки и т.д.

  • Каждый раз, когда ваша модель изменяется, вычисляйте высоту для этой строки, затем найдите ближайший тип ячейки, высота которой ближе всего к тому, что вам нужно. Сохраните этот идентификатор типа celltype с моделью. Вы также можете сохранить фиксированную высоту строки для этой ячейки в модели, чтобы вы могли вернуть ее в tableview: heightForRowAtIndexPath call (я бы не стал слишком зависеть от того, чтобы заставить его вычислять внутри самого класса ячейки - технически это не часть функциональности чертежа ячейки и более того, что используется tableview для определения типа ячейки для создания).

  • Во время выполнения, когда его попросят вернуть ячейку для этой строки, все, что вам нужно сделать, это создать (или получить из кэша ячеек) ячейку с идентификатором celltype, загрузить значения, и вам хорошо идти.

Если вычисление высоты ячейки слишком медленное, вы можете использовать тот же трюк, что и кеш таблицы, и делать это только по требованию, когда ячейка появляется в поле зрения. В любой момент времени вам нужно будет только сделать это для рассматриваемых ячеек, а затем только для одной ячейки, когда она прокручивается в один конец с любого конца.

Ответ 3

Я понимаю, что это не сработает для вас из-за бесконечного цикла, о котором вы упомянули, но у меня был некоторый успех при вызове метода layoutsubViews ячеек

Хотя это может быть немного неэффективно из-за множественных вызовов как cellForRowAtIndexPath, так и layoutSubViews, я считаю, что код чище.

-(float)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    MyCell *cell = (MyCell *)[self tableView:tableView cellForRowAtIndexPath:indexPath];

    [cell layoutSubviews];

    return CGRectGetHeight(cell.frame);
}

И в коде макета:

- (void)layoutSubviews
{
    [super layoutSubviews];

    //First expand the label to a large height to so sizeToFit isn't constrained
    [self.myArbitrarilyLengthLabel setFrame:CGRectMake(self.myArbitrarilyLengthLabel.frame.origin.x,
                                          self.myArbitrarilyLengthLabel.frame.origin.y,
                                          self.myArbitrarilyLengthLabel.frame.size.width,
                                          1000)];


    //let sizeToFit do its magic
    [self.myArbitrarilyLengthLabel sizeToFit];

    //resize the cell to encompass the newly expanded label
    [self setFrame:CGRectMake(self.frame.origin.x,
                             self.frame.origin.y,
                             self.frame.size.width,
                              CGRectGetMaxY(self.myArbitrarilyLengthLabel.frame) + 10)];
}