Автоматическое определение размера UITableViewCell в iOS 8

У меня есть подкласс UITableViewCell, который содержит многострочную метку, и я хотел бы, чтобы ячейка динамически определяла динамику на основе содержимого этой метки. Я знаю, что iOS 8 вводил автоматическую калибровку ячеек на основе ограничений AutoLayout, и я нашел несколько примеров этого уже в SO, но у меня все еще есть некоторые проблемы с реализацией этого поведения.

Здесь моя реализация updateConstraints:

- (void)updateConstraints {
    [super updateConstraints];

    [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-10-[_nameLabel(==20)]-10-[_tweetLabel]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_nameLabel, _tweetLabel)]];
    [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:[_avatarView]-10-[_nameLabel]-10-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_avatarView, _nameLabel)]];

    [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[_nameLabel]-10-[_tweetLabel]-10-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_nameLabel, _tweetLabel)]];
    [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:[_avatarView]-10-[_tweetLabel]-10-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_avatarView, _tweetLabel)]];

    [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-10-[_avatarView(==45)]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_avatarView)]];
    [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-10-[_avatarView(==45)]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_avatarView)]];
}

В контроллере табличного представления я установил высоту строки в UITableViewAutomaticDimension (и я также установил примерную высоту строки). Во время выполнения я получаю серию ошибок автоматического макета, и все ячейки представления таблиц кажутся почти полностью перекрывающимися.

Контексты автоматического компоновки заключаются между следующими ограничениями:

  • V:|-(10)-[_nameLabel]
  • V:[_nameLabel(20)]
  • V:[_nameLabel]-(10)-[_tweetLabel]
  • V:[_tweetLabel]-(10)-|
  • V:[cell(44)]

Я подозреваю, что последнее ограничение "UIView-Encapsulated-Layout-Height", которое заставляет высоту 44, является причиной проблемы, но я не совсем уверен, откуда это происходит, поэтому, надеюсь, кто-то может пролить некоторый свет в этом вопросе.

Ответ 1

Чтобы реализовать автоматическую высоту строк для ячеек таблицы, вам необходимо сделать следующее:

  • Внедрение ограничений автоматического макета внутри содержимого cellView, которые позволяют представлению выражать свою предпочтительную высоту. Обязательно установите UILabel на перенос слов на несколько строк.

    Убедитесь, что вы определили осевую цепочку ограничений в обоих измерениях, то есть ограничения, которые в совокупности связывают весь путь от одного края представления к другому. Возможно, самый простой способ убедиться, что эти ограничения верны, - это реализовать свой пользовательский контент как простой старый UIView (который легко проверить), а затем использовать ограничения, чтобы UITableViewCell.contentView обнимал это представление. (Я использую этот смысл, чтобы автоматизировать создание "ячейки для просмотра".)

  • Установить tableView.rowHeight = UITableViewAutomaticDimension

  • Установите tableView.estimatedRowHeight = 400 или какое-либо другое достаточно щедрое значение, чтобы обход некоторых ошибок UIKit, когда оценка слишком низкая.

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

Наконец, не беспокойтесь слишком много, если вы видите предупреждения о неудовлетворительных ограничениях, упоминающих "UIView-Encapsulated-Layout-Height" или аналогичные при первом загрузке табличного представления. Это артефакт начального процесса UITableView для создания ячейки, определения того, какой его размер должен быть основан на автоматических ограничениях компоновки, и сохранение UITableViewCell плотной упаковки его contentView. В репо, о котором я упоминал выше, содержится более подробное обсуждение и код для изучения этого довольно неудобного угла API.

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

Ответ 2

Я только что наткнулся на эту проблему.

Из многих других сообщений Stackoverflow они рекомендуют:

self.contentView.autoresizingMask = UIViewAutoresizingFlexibleHeight;

Это не сработало для меня сначала. Я обнаружил, что мне также нужно делать:

self.frame = CGRectMake(0, 0, self.frame.size.width, 50);

Мой пользовательский метод инициализации ячейки выглядит так:

-(id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];

    if(self)
    {
        [self initViews];
        [self initConstraints];
    }

    return self;
}

Я поместил код в свой метод initViews:

-(void)initViews
{
    ...

    // fixes an iOS 8 issue with UIViewEncapsulated height 44 bug
    self.contentView.autoresizingMask = UIViewAutoresizingFlexibleHeight;
    self.frame = CGRectMake(0, 0, self.frame.size.width, 50);
}

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

Это работает для вас?

Ответ 3

Уверены ли вы, что -translatesAutoresizingMaskIntoConstraints установлены на NO в ячейке? Если вы этого не сделаете, система генерирует ограничения на основе маски авторазмера, которая была предыдущим способом создания макета в iOS, и ее следует отключать при использовании автоматического макета.