Подтвердить что ты не робот

Множество UILabels внутри саморазмера UITableViewCell

В этом приложении iOS 8, которое я создаю, у меня есть табличное представление, и мне нужно, чтобы они меняли размер. Я реализовал его с помощью Auto Layout, и он работает. Почти. Вот как это выглядит сейчас.

enter image description here

В ячейке есть 3 ярлыка. Основной ярлык с текстом lorem ipsum. Subtitle, который имеет строку чисел (это две отдельные метки. Может быть, это путает, потому что они имеют один и тот же цвет.) Затем третий ярлык с небольшим черным текстом.

Первая метка правильно изменила размер без проблем, и вторая метка перемещается вверх и вниз соответственно. Но проблема связана с третьей небольшой меткой. Как вы можете видеть, он не изменяет размер, чтобы соответствовать всему тексту.

Теперь происходит странная вещь. Я превращаю его в ландшафт, и вот он.

enter image description here

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

enter image description here

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

Я не получаю никаких ошибок и даже не предупреждаю о своих ограничениях автомаркета.

enter image description here

Я установил эти две строки кода в методе viewDidLoad().

tableView.estimatedRowHeight = 100
tableView.rowHeight = UITableViewAutomaticDimension

Может кто-нибудь, пожалуйста, скажите мне, что я могу делать неправильно здесь?

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

Я занимаюсь автоматическими ограничениями макета, но я не могу заставить это работать. Любая помощь будет оценена.

Спасибо.


UPDATE:

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

Итак, я внес некоторые изменения.

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

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

enter image description here

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

enter image description here

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

4b9b3361

Ответ 1

Проблема здесь связана с многострочным ярлыком 'preferredMaxLayoutWidth. Это свойство, которое сообщает ярлыку, когда оно должно переноситься словами. Он должен быть правильно настроен для того, чтобы каждая метка intrinsicContentSize имела правильную высоту, что в конечном итоге означает, что Auto Layout будет использовать для определения высоты ячейки.

Xcode 6 Interface Builder ввел новую опцию, чтобы это свойство было установлено на Automatic. К сожалению, есть некоторые серьезные ошибки (с Xcode 6.2/iOS 8.2), где это неправильно установлено/автоматически при загрузке ячейки из ниба или раскадровки.

Чтобы обойти эту ошибку, нам нужно, чтобы параметр preferredMaxLayoutWidth был точно равен конечной ширине метки после ее отображения в представлении таблицы. Фактически, мы хотим сделать следующее, прежде чем возвращать ячейку из tableView:cellForRowAtIndexPath::

cell.nameLabel.preferredMaxLayoutWidth = CGRectGetWidth(cell.nameLabel.frame)
cell.idLabel.preferredMaxLayoutWidth = CGRectGetWidth(cell.idLabel.frame)
cell.actionsLabel.preferredMaxLayoutWidth = CGRectGetWidth(cell.actionsLabel.frame)

Причина, по которой просто добавление этого кода не работает, заключается в том, что, когда эти 3 строки кода выполняются в tableView:cellForRowAtIndexPath:, мы используем ширину каждой метки для установки preferredMaxLayoutWidth - однако, если вы проверите ширина меток в этот момент времени, ширина ярлыка полностью отличается от того, что будет в итоге, когда ячейка будет отображаться, и ее подпрограммы были выложены.

Как мы получим ширину меток, чтобы быть точным в этот момент, чтобы они отражали их окончательную ширину? Здесь код, который объединяет все это:

// Inside of tableView:cellForRowAtIndexPath:, after dequeueing the cell

cell.bounds = CGRect(x: 0, y: 0, width: CGRectGetWidth(tableView.bounds), height: 99999)
cell.contentView.bounds = cell.bounds
cell.layoutIfNeeded()

cell.nameLabel.preferredMaxLayoutWidth = CGRectGetWidth(cell.nameLabel.frame)
cell.idLabel.preferredMaxLayoutWidth = CGRectGetWidth(cell.idLabel.frame)
cell.actionsLabel.preferredMaxLayoutWidth = CGRectGetWidth(cell.actionsLabel.frame)

ОК, так что мы здесь делаем? Ну, вы заметите, что добавлено 3 новых строки кода. Во-первых, нам нужно установить ширину ячейки таблицы так, чтобы она соответствовала фактической ширине представления таблицы (это предполагает, что представление таблицы уже выложено и имеет свою конечную ширину, что должно быть так). Мы фактически просто делаем ширину ячейки правильной на ранней стадии, так как в этом случае табличное представление будет делать это.

Вы также заметите, что мы используем 99999 для высоты. Что это значит? Это простой способ решения проблемы, обсуждаемой подробно здесь, где, если ваши ограничения требуют больше вертикального пространства, чем текущая высота содержимого cellView, вы получаете исключение ограничения, которое фактически не указывает на какую-либо реальную проблему. Высота ячейки или любого ее подзона на самом деле не имеет значения на данный момент, потому что мы только заботимся о получении окончательной ширины для каждой метки.

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

Наконец, мы вынуждаем передать макет в ячейке, в которой механизм автоматического макета решает ваши ограничения и обновляет фреймы всех подзонов. Поскольку ячейки и contentView теперь имеют одинаковую ширину, они будут во время выполнения в представлении таблицы, ширина меток также будет правильной, что означает, что preferredMaxLayoutWidth, установленный для каждой метки, будет точным и приведет к тому, что этикетка будет завершена на правильное время, что, конечно же, означает, что высоты надписей будут правильно установлены, когда ячейка используется в виде таблицы!

Это определенно ошибка Apple в UIKit, которую мы должны решить на данный момент (поэтому, пожалуйста, отчеты об ошибках файла с Apple, чтобы они определили приоритет исправления!).

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

let cellWidth = CGRectGetWidth(tableView.bounds) - kTableViewSectionIndexWidth
cell.bounds = CGRect(x: 0, y: 0, width: cellWidth, height: 99999)

Ответ 2

Я встретил ту же проблему, что и вы и я нашли простое решение для ее решения.

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
     // dequeue cell...

     // do autolayout staffs...or if the autolayout rule has been set in xib, do nothing

     [cell layoutIfNeeded];

     return cell;
}

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

Ответ 3

Предполагая, что у вас нет ошибок с вашими ограничениями, как предлагали другие, эта проблема, похоже, связана с использованием UILabel, который позволяет использовать несколько строк в сочетании с UITableViewCellAccessory. Когда iOS выставляет ячейку и определяет высоту, она не учитывает изменение смещения в ширине, которое происходит из-за этого аксессуара, и вы получаете усечение, где вы не ожидали.

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

-(void)fixWidth:(UILabel *)label forCell:(UITableViewCell *)cell {
    float offset = 0;
    switch ([cell accessoryType]) {
        case UITableViewCellAccessoryCheckmark:
            offset = 39.0;
            break;
        case UITableViewCellAccessoryDetailButton:
            offset = 47.0;
            break;
        case UITableViewCellAccessoryDetailDisclosureButton:
            offset = 67.0;
            break;
        case UITableViewCellAccessoryDisclosureIndicator:
            offset = 33.0;
            break;
        case UITableViewCellAccessoryNone:
            offset = 0;
            break;
    }
    [label setPreferredMaxLayoutWidth:CGRectGetWidth([[self tableView]frame]) - offset - 8];
}

Просто добавьте это в свой cellForRowAtIndexPath

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    #Setup the cell
    ...
    // Fix layout with accessory view
    [self fixWidth:[cell label] forCell:cell];
    return cell;
}

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

Как упоминал smileyborg, если вы не использовали полную ширину contentView, вы могли бы ссылаться на ограничения и вычитать их также из ширины.

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

Ответ 4

Здесь у вас две проблемы.

1/Прямо сейчас, используя язык Visual Format, ваши вертикальные ограничения ячейки могут быть переведены следующим образом:

Cell: "V:|-(10)-[nameLabel]-(67)-|"

Затем вы устанавливаете вторую группу ограничений:

Cell: "V:|-(10)-[nameLabel]-(8)-[pnrLabel]-(2)-[actionsLabel]"

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

2/По некоторым причинам actionsLabel ограничивается одной строкой при запуске приложения. Затем, когда вы поворачиваете свое устройство в альбомном режиме, actionsLabel принимает отображаемые значения с двумя строками или более. Затем, когда вы поворачиваете устройство обратно в портретный режим, actionsLabel продолжает отображать две строки или больше. Но поскольку actionsLabel на самом деле не является частью ограничений высоты вашей ячейки, он перекрывает границы ваших ячеек.

Если вы хотите решить все эти проблемы, я сначала рекомендую вам перестроить ваш xib файл с нуля. Это вылечит странное поведение actionsLabel (две строки и более только при повороте устройства).

Затем вам нужно будет определить свои ограничения следующим образом:

Cell: "V:|-(10)-[nameLabel(>=21)]-(8)-[pnrLabel(>=21)]-(2)-[actionsLabel(>=21)]-(10)-|"

Конечно, вы можете определить другие минимальные ограничения высоты для ярлыков, чем (>=21). Точно так же ваше нижнее поле может быть установлено на другое значение, чем -(10)-.

Добавление

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

enter image description here

Ответ 5

Я попробовал очень простое и элегантное решение "fogisland" - и это не сработало. К счастью, я узнал, что одна дополнительная линия заставляет его работать в самых разных направлениях. Просто сообщите системе, что вы предлагаете не только новый макет (layoutIfNeeded), вы явно запрашиваете его (setNeedLayout)

    cell.setNeedsLayout()
    cell.layoutIfNeeded()
    return cell