Всем доброго времени суток, недавно столкнулся с такой проблемой, что при записи текста (длинного текста) в ячейку таблицы, её 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

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

Спасибо за внимание, надеюсь, что смог помочь хоть чем то.

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


  1. maaGames
    13.08.2015 11:13
    +1

    Будь зайкой, почикай обе картинки с полосатой пустотой на всю высоту экрана, пока тебя не покарали те, у кого каралка большая.)


  1. numen31337
    13.08.2015 11:37
    +6

    Думаю данная статья устарела лет так на пару вместе с хардкодом ширины экрана, dequeueReusableCellWithIdentifier без indexPath и т.д. :)
    Рекомендую автору ознакомится тут например


  1. StrangerInRed
    13.08.2015 11:39
    +3

    Только все это стремительно становится ненужно, когда юpаешь constraints. И там появляются другие подходы.


  1. Ogi
    13.08.2015 12:29
    +3

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

    Чего мелочиться, пишите сразу «Калькуляция хайта селла в динамик тэйбле UITableView».


    1. StrangerInRed
      13.08.2015 12:50
      -2

      Калькуляция — вполне словарное слово.


  1. usgleb
    13.08.2015 14:07
    +5

    Недельный поиск???


  1. KamiSempai
    13.08.2015 14:11
    +1

    Странно, что на поиск решения у вас ушла неделя. Как по мне, так оно «гуглица» за несколько минут.
    Касаемо кода:

    UIFont* font = [UIFont systemFontOfSize:17.f];
    
    Это серьезно? А если шрифт у Label в Story Board окажется другого размера или вообще другим? Про магическое число 320 вообще молчу.

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


  1. superanreal
    13.08.2015 14:15
    +3

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

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


    Хотя это не избавляет от проблем с многострочными UILabel. В довесок Apple не исправила баг с скачущими высотами ячеек при прокрутке вверх (тык).


  1. Lovesuper
    13.08.2015 17:22

    Статья — хлам. Уже давно считают высоту ячейки через AutoLayout.


    1. sonicCat
      24.08.2015 23:05

      И у вас не тормозит?


    1. Naftic
      27.08.2015 19:24

      На сколько я знаю, через autolayout — считает очень медленно при сколь-нибудь сложном устройстве ячеек.

      Я правда не очень понимаю, что означает

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

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


      1. superanreal
        04.09.2015 12:31

        При правильной настройки constraints и выставлении

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

        TableView сам рассчитает размер ячейки, достаточный для ее отображения.


        1. Naftic
          08.09.2015 10:23

          Оно долго считает, если ячейки сложные. Так или иначе, приходится в ручную считать.


          1. superanreal
            08.09.2015 10:28

            Что значит вручную? Без использования constraints?
            Можно кэшировать уже рассчитанные высоты и в -estimatedHeightForRowAtIndexPath их возвращать.


            1. Naftic
              08.09.2015 15:27

              Да, без использования констраинтов для расчета. Получается в разы быстрей.


              1. superanreal
                08.09.2015 15:37

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


                1. Naftic
                  09.09.2015 18:01

                  Пример, когда это необходимо: гипотетическое новостное приложением; скажем новости делятся на 4-5 видов и в них используется UITextView (не фиксированной высоты) + до 20 различных view. Соотвественно высота ячейки зависит от высоты TextView (может еще быть несколько Label-ов не фиксированной высоты и ширины). Тогда будет тормозить, знаю на своем опыте.

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

                  Да, когда поменяется ориентация — вам придется заново все пересчитывать и если лэйауты отличаются, то возни будет достаточно. Никто и не обещал, что будет легко:)


                  1. superanreal
                    10.09.2015 15:39

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


                    1. niveus_everto
                      10.09.2015 21:08

                      А не подскажете гугл запрос, где можно посмотреть примеры 3-5 лейблов в ячейке.
                      Сколько не пробовал — не попадается более-менее подробного гайда.

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

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

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