UITableView гибкий / динамический heightForRowAtIndexPath


случае

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

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

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

представьте себе UITableViewCell, который содержит метку с именем textLabel. Внутри cellForRowAtIndexPath делегировать метод мы размещаем textLabel.numberOfLines = 0, что в основном говорит о том, что метка может иметь столько строк, сколько ей нужно чтобы представить текст для определенной ширины. Проблема возникает, если мы даем textLabel текст больше, чем ширина, первоначально заданная textLabel. Появится вторая строка, но высота ячейки не будет автоматически отрегулирована, и поэтому мы получим беспорядочный вид таблицы.

как было сказано ранее, мы можем использовать sizeWithFont для расчета высоты, но он должен знать, какой шрифт используется, что ширина и т. д. Если по причинам простоты мы просто заботимся о ширине, мы могли бы жестко закодировать что ширина будет около 320.0 (не принимая во внимание прокладку). Но что произойдет, если мы используем UITableViewStyleGrouped вместо plain ширина будет около 300.0 и ячейка снова будет перепутана. Или что происходит, если мы переключаемся с портрета на пейзаж, у нас гораздо больше места, но оно не будет использоваться, так как мы жестко закодировали 300.0.

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

Мои Собственные Мысли

вы могли бы позвонить cellForRowAtIndexPath метод, который принадлежит классу UITableView, чтобы получить ячейку для определенного раздела и строки. Я прочитал пару сообщений, в которых говорилось, что вы не хотите этого делать, но я действительно не понимаю этого. Да, я согласен, что он уже выделит ячейку, но heightForRowAtIndexPath делегат метод вызывается только для ячеек, которые будут видны, так что ячейка будет выделена в любом случае. Если вы правильно используете dequeueReusableCellWithIdentifier ячейки не будет выделяться снова в cellForRowAtIndexPath метод, вместо этого используется указатель и свойства просто настраиваются. Тогда в чем проблема?

обратите внимание, что ячейка не рисуется внутри cellForRowAtIndexPath делегировать метод, когда ячейка представления таблицы становится видимой скрипт вызовет setNeedDisplay метод на UITableVieCell, который запускает drawRect метод для рисования ячейки. Так что зовите cellForRowAtIndexPath делегат напрямую не потеряет производительность, потому что его нужно нарисовать дважды.

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

возможно, вы можете создать свой собственный sizeForCell метод, который проходит через все варианты, что если клетка в стиле значение1 или значение2, и т. д.

Вывод/Вопрос

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

7 60

7 ответов:

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

Это неверно. Табличное представление должно вызывать heightForRowAtIndexPath (Если это реализовано) для всех строк, которые находятся в табличном представлении, а не только те, которые в настоящее время отображаются. Причина в том, что он должен выяснить свою общую высоту, чтобы отобразить правильный свиток показатели.

раньше я делал это с помощью:

  1. создание объектов коллекции (массив информации о размере (словарь, NSNumber высот строк и т. д.) на основе объектов коллекции, которые будут использоваться для представления таблицы.

  2. Это делается, когда мы обрабатываем данные из локального или удаленного источника.

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

  4. эти объекты коллекции будут использоваться каждый раз, когда я реализую протоколы UITableViewDataSource или UITableViewDelegate для определения размеров экземпляров UITableViewCell и его подвидов и т. д.

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

не использовать абсолютное значение для инициализации кадров. Используйте относительное значение, основанное на текущей ориентации и границах.

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

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

вот мой подход для решения этой

  1. Я предполагаю, что в этом решении только одна метка имеет "динамическую" высоту
  2. Я также предполагаю, что если мы сделаем метку автоматического размера, чтобы растянуть высоту, поскольку ячейка растет, только высота ячейки необходима для изменения
  3. Я предполагаю, что перо имеет соответствующий интервал для того, где метка будет и сколько места выше и ниже его
  4. мы не хотим менять код каждый раз, когда мы изменить шрифт или положение метки в наконечнике

Как обновить высота:

-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {

    // We want the UIFont to be the same as what is in the nib,
    //  but we dont want to call tableView dequeue a bunch because its slow. 
    //  If we make the font static and only load it once we can reuse it every
    //  time we get into this method
    static UIFont* dynamicTextFont;
    static CGRect textFrame;
    static CGFloat extraHeight;
    if( !dynamicTextFont ) {
        DetailCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
        dynamicTextFont = cell.resizeLabel.font;
        CGRect cellFrame = cell.frame;
        textFrame = cell.resizeLabel.frame;
        extraHeight = cellFrame.size.height-textFrame.size.height; // The space above and below the growing field
    }
    NSString* text = .... // Get this from the some object using indexPath 

    CGSize  size = [text sizeWithFont:dynamicTextFont constrainedToSize:CGSizeMake(textFrame.size.width, 200000.f) lineBreakMode:UILineBreakModeWordWrap];

    return size.height+extraHeight;
}

вопросы:

  • если вы не используете ячейку прототипа, вам нужно будет проверить, является ли ячейка нулевой и инициализировать ее
  • ваш наконечник / раскадровка должны иметь авторазмер UILabel и иметь несколько строк, установленных в 0

вы должны взглянуть на TTTableItemCell.m в рамках Three20. Он следует другому подходу, в основном, имея каждый класс ячеек (с некоторыми предопределенными настройками, такими как шрифт, макет и т. д.) реализовать общий метод + tableView: sizeForItem: (или что-то в этом роде), где он получает переданный текст в объекте item. Когда вы ищете текст для определенной ячейки, вы также можете найти соответствующий шрифт.

относительно высоты ячейки: вы можете проверить ширину вашего tableView и, если необходимо, вычесть поля на UITableViewStyleGrouped и ширина возможного индексного бара и элемента раскрытия (который вы ищете в хранилище данных для данных ваших ячеек). Когда ширина tableView изменяется, например, вращением интерфейса, вы должны вызвать [tableView reloadData].

чтобы ответить на вопрос оригинальный плакат спросил, который был " это нормально, чтобы позвонить cellForRowAtIndexPath?- это не так. Это даст вам ячейку, но она не будет выделять ее этому indexPath внутренне, и она не будет повторно поставлена в очередь (нет способа вернуть ее), поэтому вы просто потеряете ее. Я предполагаю, что это будет в autorelease пул и будут в конце концов освобождены, но вы все равно будете создавать множество клеток снова и снова и это действительно довольно расточительно.

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

у меня есть идея о динамической высоте ячейки.

просто создайте один экземпляр вашей пользовательской ячейки в качестве переменной-члена UITableViewController. В tableView:heightForRowAtIndexPath: метод устанавливает содержимое ячейки и возвращает высоту ячейки.

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

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

tableView:heightForRowAtIndexPath: тело функции будет выглядеть так:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return [MyCell cellHeightForContent:yourContent];
}

вот мое решение, которое я использовал для реализации некоторых довольно гладких ячеек для приложения чата.

до этого момента я всегда был очень раздражен heightForCellAtIndexPath: потому что это приводит к нарушению сухого принципа. С помощью этого решения мой heightForRowAtIndexPath: стоит 1,5 МС на ячейку, которую я мог бы сбривать до ~1 мс.

в принципе, вы хотите, чтобы каждый подвид внутри вашей ячейки реализовывал sizeThatFits: создайте закадровую ячейку, которую вы затем настройте запрос корневого представления с помощью sizeThatFits: CGSizeMake(tableViewWidth, CGFLOAT_MAX).

есть несколько подводных камней на этом пути. Некоторые представления UIKit имеют дорогостоящие операции установки. Например - [UITextView setText] делает много работы. Трюк здесь состоит в том, чтобы создать подкласс, буферизовать переменную, а затем переопределить setNeedsDisplay для вызова -[super setText:], когда представление будет отображаться. Конечно, вам придется реализовать свой собственный sizeThatFits: использование программирования с использованием UIKit увеличение.