Как правильно сделать ячейки таблицы переменной высоты на iPhone? - PullRequest
22 голосов
/ 18 сентября 2009

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

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

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

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

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

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

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

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

Мое предлагаемое решение:

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

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

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

Так что об этой возможности не может быть и речи.

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

ячейки Messed Table http://jamsoftonline.com/images/messed_table_cells.png

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

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

Так что любая помощь здесь будет с благодарностью.

Ответы [ 3 ]

38 голосов
/ 05 февраля 2010

Вот что я использую. В 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 голосов
/ 18 сентября 2009

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

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

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

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

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

0 голосов
/ 09 декабря 2013

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