it-swarm.com.ru

Несколько UILabels внутри саморазмерного UITableViewCell

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

enter image description here

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

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

Теперь происходит странная вещь. Я переворачиваю это пейзаж и вот оно.

enter image description here

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

enter image description here

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

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

enter image description here

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

tableView.estimatedRowHeight = 100
tableView.rowHeight = UITableViewAutomaticDimension

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

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

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

Спасибо.


Обновление:

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

Поэтому я внес некоторые изменения.

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

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

enter image description here

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

enter image description here

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

57
Isuru

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

Интерфейсный Разработчик Xcode 6 представил новую опцию, чтобы установить для этого свойства значение Автоматически. К сожалению, есть некоторые серьезные ошибки (по состоянию на 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 для высоты. О чем это? Это простой обходной путь для проблемы, подробно обсуждаемой здесь , где, если вашим ограничениям требуется больше вертикального пространства, чем текущая высота ячейки contentView, вы получите исключение ограничения, которое фактически не указывает на реальную проблему. Высота ячейки или любого из ее подпредставлений на самом деле не имеет значения, поскольку нам нужно только получить окончательную ширину для каждой метки.

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

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

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

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

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

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

- (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;
}

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

49
fogisland

Предполагая, что у вас нет ошибок с вашими ограничениями, как предлагали другие, эта проблема, по-видимому, связана с использованием 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;
}

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

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

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

6
user2898617

У вас есть две проблемы здесь.

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

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

4
Imanou Petit

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

    cell.setNeedsLayout()
    cell.layoutIfNeeded()
    return cell
1
Hardy_Germany