Пользователь
0,0
рейтинг
13 августа 2015 в 11:00

Разработка → Калькуляция высоты ячейки в динамической таблице UITableView из песочницы

Всем доброго времени суток, недавно столкнулся с такой проблемой, что при записи текста (длинного текста) в ячейку таблицы, её Label не увеличивается пропорционально объёму текста, то есть если там 3 слова, то все хорошо, но если записать туда 3-4 предложения, то появится примерно вот такая штука.



Недельный поиск решения проблемы оказался все таки успешным и сегодня постараюсь объяснить вам, как именно решать данную проблему.

После того, как вы создали NavigationController, сделали свой ViewController наследником от UITableViewController'а, нам надо создать класс для нашей кастомной ячейки. Создаем новый класс называем его например customCell и указываем, что он наследник класса UITableViewCell.

#import "customCell.h"

@implementation customCell

- (void)awakeFromNib {
    // Initialization code
}

- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
    [super setSelected:selected animated:animated];

    // Configure the view for the selected state
}

В файле *.m сразу создадутся 2 метода, которые в нашем случае нам не понадобятся.

Далее помещаем в наш прототип ячейки UILabel. Кликаем на него мышкой и устанавливаем такие настройки, важно, чтобы вот эти параметры были именно такими.



Lines = 0 говорит о том, что ячейка может иметь неограниченное количество строк, а Line Break = Word Wrap, что слова будут переноситься на новую строку, когда закончится место в этой. Но это еще не все, теперь нам надо связать наш UILabel с файлом customCell.h

После этого вернемся в файл ViewController и воспользуемся делегатом heightForRowAtIndexPath.

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

Можно проводить калькуляцию в этом делегате прямо в вашем ViewController'е, но я бы советовал перенести все это в customCell.m для экономии места в основном файле.

Перейдем в customCell.h и создадим метод с плюсиком.

+(CGFloat) heightForText:(NSString*) text;


И вернемся к файлу .m для его реализации.

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

boundingRectWithSize

в него мы записываем длину ячейки, с отступами или без, высоту (если нужно ее ограничить) и «засунуть» вашу Dictionary с атрибутами. Пример кода ниже.

+(CGFloat) heightForText:(NSString*) text {
    
    CGFloat offset = 1.0;
    
    UIFont* font = [UIFont systemFontOfSize:17.f];
    
    NSMutableParagraphStyle* paragraph = [[NSMutableParagraphStyle alloc] init];
    [paragraph setLineBreakMode:NSLineBreakByWordWrapping];    
    
    NSDictionary* attributes =
    [NSDictionary dictionaryWithObjectsAndKeys:
     font , NSFontAttributeName,
     paragraph, NSParagraphStyleAttributeName, nil];
    
    CGRect rect = [text boundingRectWithSize:CGSizeMake(320 - 2 * offset, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading attributes:attributes context:nil];
    
    return CGRectGetHeight(rect) + 2 * offset;
    
}

Теперь возвращаемся во ViewController и в делегате с помощью return вызвать метод класса cusomCell и получить от него информацию о высоте вашей ячейки.

-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    
    return [customCell heightForText: self.textStringHabr];
    
}

Проверяем.



Всё работает!

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

-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return 1;
}


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    
    static NSString* identifier = @"cell";
    
    customCell* cell = [tableView dequeueReusableCellWithIdentifier:identifier];
    
    if (!cell) {
        cell = [[customCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
    }
    
    cell.testTextLabel.text = self.textStringHabr;
    
    return cell;
}


Для тех, кто пишет на Swift, всё гораздо проще, Apple избавила нас от этого написания кода и там все делается с помощью всего лишь двух строчек кода.

self.tableView.rowHeight = UITableViewAutomaticDimension
        
self.tableView.estimatedRowHeight = 44.0

Информация взята из уроков Алексея Скутаренко.

Спасибо за внимание, надеюсь, что смог помочь хоть чем то.
@tupoi
карма
3,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (19)

  • +1
    Будь зайкой, почикай обе картинки с полосатой пустотой на всю высоту экрана, пока тебя не покарали те, у кого каралка большая.)
  • +6
    Думаю данная статья устарела лет так на пару вместе с хардкодом ширины экрана, dequeueReusableCellWithIdentifier без indexPath и т.д. :)
    Рекомендую автору ознакомится тут например
  • НЛО прилетело и опубликовало эту надпись здесь
  • +3
    Калькуляция высоты ячейки в динамической таблице UITableView

    Чего мелочиться, пишите сразу «Калькуляция хайта селла в динамик тэйбле UITableView».
    • НЛО прилетело и опубликовало эту надпись здесь
  • +5
    Недельный поиск???
  • +1
    Странно, что на поиск решения у вас ушла неделя. Как по мне, так оно «гуглица» за несколько минут.
    Касаемо кода:
    UIFont* font = [UIFont systemFontOfSize:17.f];
    
    Это серьезно? А если шрифт у Label в Story Board окажется другого размера или вообще другим? Про магическое число 320 вообще молчу.

    И на последок, хотелось бы заметить, что это частный случай, когда ячейка состоит только из одного текстового поля. Для более сложных ячеек такой способ не подходит.
  • +3
    Статья по-большей части даже вредная. Как минимум, надо указать, что так никто не делает с тех пока, как появился autolayout.
    В iOS 6-7 задают constraints и высчитывают высоту contentView методом -systemLayoutSizeFittingSize.
    В iOS 7 появился доп. метод делегата таблицы -estimatedHeightForRowAtIndexPath, для приблизительного указания высоты.
    Ну и счастливчики, разрабатывающие под iOS8+ могу использовать динамический подсчет величины просто указав

    self.tableView.rowHeight = UITableViewAutomaticDimension;
    self.tableView.estimatedRowHeight = EstimatedRowHeight;
    


    Хотя это не избавляет от проблем с многострочными UILabel. В довесок Apple не исправила баг с скачущими высотами ячеек при прокрутке вверх (тык).
  • 0
    Статья — хлам. Уже давно считают высоту ячейки через AutoLayout.
    • 0
      И у вас не тормозит?
    • 0
      На сколько я знаю, через autolayout — считает очень медленно при сколь-нибудь сложном устройстве ячеек.

      Я правда не очень понимаю, что означает
      Уже давно считают высоту ячейки через AutoLayout.

      Ведь для этого ячейка должна быть создана, а высоту нужно передать до создания ячейки…
      • 0
        При правильной настройки constraints и выставлении
        self.tableView.rowHeight = UITableViewAutomaticDimension;
        self.tableView.estimatedRowHeight = EstimatedRowHeight;
        

        TableView сам рассчитает размер ячейки, достаточный для ее отображения.
        • 0
          Оно долго считает, если ячейки сложные. Так или иначе, приходится в ручную считать.
          • 0
            Что значит вручную? Без использования constraints?
            Можно кэшировать уже рассчитанные высоты и в -estimatedHeightForRowAtIndexPath их возвращать.
            • 0
              Да, без использования констраинтов для расчета. Получается в разы быстрей.
              • 0
                На счет скорости вполне возможно, но хотелось бы увидеть приблизительный лэйаут на котором может сильно тормозить расчет высот.
                Но как быть с ориентацией, и случаями, когда от размера зависит положение и размер элементов? Констреинты очень сильно в таких случаях помогают.
                • 0
                  Пример, когда это необходимо: гипотетическое новостное приложением; скажем новости делятся на 4-5 видов и в них используется UITextView (не фиксированной высоты) + до 20 различных view. Соотвественно высота ячейки зависит от высоты TextView (может еще быть несколько Label-ов не фиксированной высоты и ширины). Тогда будет тормозить, знаю на своем опыте.

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

                  Да, когда поменяется ориентация — вам придется заново все пересчитывать и если лэйауты отличаются, то возни будет достаточно. Никто и не обещал, что будет легко:)
                  • 0
                    4-5 новостей — это 4-5 ячеек с различной разметкой и reuseIdentifier.
                    20 вью — это в одной ячейке заголовка новости? Если так — то очевидны проблемы с интерфейсом.
                    Я сам сейчас делаю приложение с выводом множества ячеек с 3-5 label различной высоты. Использую Masonry и авторасчет высот. За раз показывается около 40-60 ячеек. Не тормозит даже на iPhone 5, на 4s — слегка.
                    • 0
                      А не подскажете гугл запрос, где можно посмотреть примеры 3-5 лейблов в ячейке.
                      Сколько не пробовал — не попадается более-менее подробного гайда.

                      Сам пользуюсь Masonry, но ячейки продолжают плясать через раз.

                      ЗЫ. аналогично хотелось бы посмотреть по поводу Section Header с автолайоутом.

                      ЗЗЫ. таргет сборки иос 7+

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