iOS: многострочная UILabel в автоматическом макете

183

У меня возникли проблемы с попыткой добиться базового поведения макета с помощью Auto Layout. Мой контроллер вида выглядит так в IB:

введите описание изображения здесь

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

Как я могу изменить размер заголовка, чтобы он соответствовал количеству строк, требуемому для строки? Насколько я понимаю, я не могу использовать методы setFrame, потому что я использую Auto Layout. И я должен использовать Auto Layout, потому что мне нужно, чтобы эти другие представления оставались ниже метки заголовка (отсюда и ограничения).

Как я могу это сделать?

Джеймс Харп
источник
3
Я также борюсь с подобной проблемой. Но я все еще изо всех сил пытаюсь заставить верхнюю метку динамически регулировать свою высоту, чтобы соответствовать содержанию. Как ты этого добился?
jbandi
1
Пожалуйста, пометьте ответ @ mwhuss как принятый.
Джеммонс
1
Достигли ли вы требуемого результата?
Анируддха
проверьте это, вам не нужно добавлять одну строку кода stackoverflow.com/a/36862795/4910767
Badal Shah

Ответы:

254

Использование -setPreferredMaxLayoutWidthна UILabelи autoLayout должно обрабатывать все остальное.

[label setPreferredMaxLayoutWidth:200.0];

См. Документацию UILabel для предпочитаемого MaxLayoutWidth .

Обновить:

Необходимо только установить heightограничение в раскадровке Greater than or equal to, нет необходимости устанавливатьPreferredMaxLayoutWidth.

mwhuss
источник
8
Хороший, в любом случае, чтобы сделать это в конструкторе интерфейсов?
Sjoerd исполняет
12
Также установите ограничение высоты в IB больше или равно ".
Джоу
10
Напоминание: пожалуйста, не забудьте установить свойство numberOfLines.
Ли Фумин
2
Да, используйте как хотите максимальную ширину.
mwhuss
4
См stackoverflow.com/a/29180323/1529675 о том , как определить preferredMaxLayoutWidthи увидеть devetc.org/code/2014/07/07/auto-layout-and-views-that-wrap.html для краткой , но информативной рецензию, в комплекте с анимированные GIF-файлы (не моя рецензия, я нашел ее через Google)
tboyce12
213

Увеличьте количество строк в вашем наборе меток до 0, а также, что более важно, для высоты набора автоматического макета до> = x. Авто макет сделает все остальное. Вы также можете содержать ваши другие элементы, основанные на предыдущем элементе, чтобы затем правильно расположить.

автоматическая разметка

Чарли Ву
источник
Если этот метод не работает для вас, пожалуйста, посмотрите ответ Кори Имдике о том, что последующие представления не запрещают Auto Layout изменять размеры (возможно) многострочного представления.
Том Ховард,
1
Это единственный ответ, который работал для меня в ячейке табличного представления. Также работает с фиксированным количеством строк (вместо 0 / без ограничений)
bgolson
Это не сработало для меня с явно заданным предпочтительным значением максимальной ширины, поэтому убедитесь, что в IB этот параметр установлен как автоматический при использовании этого метода. Ура
JMC
Настройка автоматической максимальной ширины доступна только в iOS8. Кроме того, насколько я понимаю, он будет автоматически регулировать высоту, и, по моему опыту, вам не нужно устанавливать ограничение высоты для его работы. Это работало для меня, используя явную ширину.
Тони
1
Это не сработало для меня. Я считаю, что stackoverflow.com/a/13032251/1529675 является правильным ответом.
tboyce12
48

Источник: http://www.objc.io/issue-3/advanced-auto-layout-toolbox.html.

Размер внутреннего содержимого многострочного текста

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

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

- (void)layoutSubviews
{
    [super layoutSubviews];
    myLabel.preferredMaxLayoutWidth = myLabel.frame.size.width;
    [super layoutSubviews];
}

Первый вызов [super layoutSubviews] необходим для метки, чтобы получить набор кадров, а второй вызов необходим для обновления макета после изменения. Если мы опускаем второй вызов, мы получаем ошибку NSInternalInconsistencyException, потому что мы внесли изменения в проход макета, которые требуют обновления ограничений, но мы не запускали макет снова.

Мы также можем сделать это в самом подклассе меток:

@implementation MyLabel
- (void)layoutSubviews
{
    self.preferredMaxLayoutWidth = self.frame.size.width;
    [super layoutSubviews];
}
@end

В этом случае нам не нужно сначала вызывать [super layoutSubviews], потому что когда вызывается layoutSubviews, у нас уже есть рамка на самой метке.

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

- (void)viewDidLayoutSubviews
{
    [super viewDidLayoutSubviews];
    myLabel.preferredMaxLayoutWidth = myLabel.frame.size.width;
    [self.view layoutIfNeeded];
}

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

Антон Матосов
источник
спасибо, спасибо, спасибо вам не только за код, но, что более важно, за объяснение и примеры того, как это сделать не только в подклассе, но и из контроллера представления.
Джибути33
я пытаюсь это сделать в моем контроллере представления, и viewDidLayoutSubviews вызывается после viewWillAppear и viewDidAppear. После viewDidAppear возникает вспышка, когда размеры ярлыков корректно изменяются. Есть ли способ избежать этого? Есть ли способ для изменения размера, прежде чем пользователь увидит что-нибудь? в моем случае мои многострочные метки находятся в tableHeaderView, и поэтому я также изменяю высоту представления заголовка на основе меток, используя этот пример ( stackoverflow.com/questions/20982558/… )
djibouti33
@ djibouti33 Можете ли вы предоставить минимальный пример кода (например, в формате github repo), чтобы я мог легко проверить вашу проблему?
Антон Матосов
Спасибо @Антон. Наш дизайн с тех пор переключился с UITableView на UICollectionView, и, к счастью, я не видел этого поведения. Если я смогу открыть историю Git для создания небольшого примера проекта, я дам вам знать.
Джибути33
Проблема для меня, так как я начал работать на iOS 9 sim, но когда я тестировал на iOS 8, то проблема началась. Мне нужно установить максимальную ширину вручную.
Наз
17

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

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

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

IB Menu

Если представление застряло, следуйте за представлениями, которые находятся ниже него, и убедитесь, что у одного из них нет верхнего пространства для ограничения суперпредставления. Затем просто убедитесь, что параметр количества строк для метки установлен в 0, и он должен позаботиться об остальном.

Кори Имдике
источник
Да, после долгих тренировок я смог получить тот эффект, который хотел. Вы просто должны очень тщательно продумать ограничения, которые вы хотите. Это не помогает, что IB постоянно добавляет ограничения, которые вы не ожидаете.
Джеймс Харп
5

Я считаю, что вам нужно следующее:

  • Верхнее ограничение
  • Ведущее ограничение (например, левая сторона)
  • Трейлинг-ограничение (например, правая сторона)
  • Установите приоритет размещения контента, горизонтальный на низкий, чтобы он занимал заданное пространство, если текст короткий.
  • Установите сопротивление сжатию содержимого, горизонтальное на низкое, чтобы оно не перекрывалось, а пыталось расширяться.
  • Установите количество строк в 0.
  • Установите режим разрыва строки на перенос слов.
Крис
источник
Это должно работать в xcode9. Мне не нужно было ничего делать с ограничением высоты метки (у меня его нет): в последнее время он работает только из коробки, как только метка будет ограничена (трейлинг, ведущий, верх и низ). Уикит делает все остальное
Антон Тропашко
Приоритет объятия контента был моей проблемой. Спасибо, что сообщили мне об этом свойстве, а также о сопротивлении сжатию. Вы помогли мне решить многочасовую проблему.
Дэвид Гей
0

Ни одно из различных решений, найденных во многих темах по теме, не подошло идеально для моего случая (x динамических многострочных меток в ячейках динамического табличного представления).

Я нашел способ сделать это:

Установив ограничения для вашей метки и установив для его свойства multiline значение 0, создайте подкласс UILabel; Я назвал мой AutoLayoutLabel:

@implementation AutoLayoutLabel

- (void)layoutSubviews{
    [self setNeedsUpdateConstraints];
    [super layoutSubviews];
    self.preferredMaxLayoutWidth = CGRectGetWidth(self.bounds);
}

@end
Танги Г.
источник
0

У меня есть UITableViewCell, который имеет текстовую метку переноса. Я работал над переносом текста следующим образом.

1) Установите ограничения UILabel следующим образом.

введите описание изображения здесь

2) Установить № строк до 0.

3) Добавлено ограничение высоты UILabel в UITableViewCell.

@IBOutlet weak var priorityLabelWidth: NSLayoutConstraint!

4) На UITableViewCell:

priorityLabel.sizeToFit()
priorityLabelWidth.constant = priorityLabel.intrinsicContentSize().width+5
AG
источник
-12

Один из способов сделать это ... По мере увеличения длины текста, попытайтесь изменить (уменьшить) размер шрифта текста надписи, используя

Label.adjustsFontSizeToFitWidth = YES;
user1662392
источник
1
Я хочу, чтобы различные экземпляры контроллера представления имели одинаковый внешний вид, поэтому я не хочу менять размер шрифта.
Джеймс Харп