UILabel sizeToFit не работает с autolayout ios6

Как я должен программно настроить (и каким способом) UILabel, высота которого зависит от его текста? Я пытался настроить его, используя комбинацию Storyboard и кода, но безрезультатно. Каждый рекомендует sizeToFit при настройке lineBreakMode а также numberOfLines, Тем не менее, независимо от того, если я положил этот код в viewDidLoad:, viewDidAppear:, или же viewDidLayoutSubviews Я не могу заставить его работать. Либо я делаю поле слишком маленьким для длинного текста, и оно не увеличивается, либо я делаю его слишком большим и оно не уменьшается.

13 ответов

Решение

Обратите внимание, что в большинстве случаев решение Мэтта работает как положено. Но если это не работает для вас, пожалуйста, читайте дальше.

Чтобы ваша метка автоматически меняла высоту, вам нужно сделать следующее:

  1. Установить ограничения макета для метки
  2. Установить ограничение по высоте с низким приоритетом. Должно быть ниже, чем ContentCompressionResistancePriority
  3. Установите numberOfLines = 0
  4. Установите ContentHuggingPriority выше приоритета высоты надписи
  5. Установите предпочитаемый MaxLayoutWidth для метки. Это значение используется меткой для расчета его высоты

Например:

self.descriptionLabel = [[UILabel alloc] init];
self.descriptionLabel.numberOfLines = 0;
self.descriptionLabel.lineBreakMode = NSLineBreakByWordWrapping;
self.descriptionLabel.preferredMaxLayoutWidth = 200;

[self.descriptionLabel setContentHuggingPriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical];
[self.descriptionLabel setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical];
[self.descriptionLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
[self addSubview:self.descriptionLabel];

NSArray* constrs = [NSLayoutConstraint constraintsWithVisualFormat:@"|-8-[descriptionLabel_]-8-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(descriptionLabel_)];
[self addConstraints:constrs];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-8-[descriptionLabel_]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(descriptionLabel_)]];
[self.descriptionLabel addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[descriptionLabel_(220@300)]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(descriptionLabel_)]];

Использование Interface Builder

  1. Установите четыре ограничения. Ограничение по высоте является обязательным. введите описание изображения здесь

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

  3. Перейдите в инспектор размера метки и увеличьте вертикальный ContentHuggingPriority и вертикальный ContentCompressionResistancePriority.
    введите описание изображения здесь

  4. Выберите и измените ограничение высоты.
    введите описание изображения здесь

  5. И уменьшить приоритет ограничения высоты.
    введите описание изображения здесь

Наслаждаться. :)

В iOS 6, при использовании автоматического размещения, если стороны (или ширина) UILabel закреплены, он будет автоматически увеличиваться и уменьшаться по вертикали, чтобы соответствовать его содержимому, без какого-либо кода и без нарушения его сопротивления сжатию или чего-либо еще. Это очень просто.

В более сложных случаях просто установите метку preferredMaxLayoutWidth,

В любом случае, правильная вещь происходит автоматически.

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

Первое, что нужно забыть sizeToFit, Auto Layout будет обрабатывать это от вашего имени в зависимости от внутреннего размера контента.

Поэтому проблема заключается в том, как сделать так, чтобы метка соответствовала ее содержанию с помощью Auto Layout? В частности - потому что вопрос упоминает это - высота. Обратите внимание, что те же принципы применяются к ширине.

Итак, давайте начнем с примера UILabel, высота которого равна 41px:

Как вы можете видеть на скриншоте выше, "This is my text" имеет обивку сверху и снизу. Это отступ между высотой UILabel и его содержанием, текстом.

Если мы запустим приложение в симуляторе, то увидим то же самое:

Теперь давайте выберем UILabel в Интерфейсном Разработчике и посмотрим на настройки по умолчанию в Инспекторе размера:

Обратите внимание на выделенное ограничение выше. Это и есть приоритет содержания контента. Как Эрика Садун описывает это в превосходной демо- версии Auto Layout iOS, это:

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

Для нас с UILabel основным содержанием является текст.

Вот мы и подошли к сути этого базового сценария. Мы дали нашей текстовой метке два ограничения. Они конфликтуют. Один говорит, что "высота должна быть равна 41 пикселю в высоту". Другой говорит: "Обними представление о его содержимом, чтобы у нас не было лишних отступов". В нашем случае обнимите вид текста, чтобы у нас не было лишних отступов.

Теперь, с помощью Auto Layout, с двумя разными инструкциями, которые говорят, делают разные вещи, среда выполнения должна выбирать одну или другую. Это не может сделать оба. UILabel не может иметь высоту 41 пикселя и не иметь отступов.

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

Итак, давайте попробуем. Мое ограничение по высоте имеет приоритет 1000, что является обязательным. Высота содержания контента составляет 250, что является слабым. Что произойдет, если мы уменьшим приоритет ограничения высоты до 249?

Теперь мы видим, как волшебство начинает происходить. Давайте попробуем в симе:

Потрясающие! Содержание обнимается Только потому, что приоритет 249 высоты меньше, чем приоритет 250 объятия контента. По сути, я говорю "высота, которую я здесь указываю, менее важна, чем та, которую я указал для объятия контента". Итак, контент, обнимающий, побеждает.

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

Оставим делать эквивалент ширины в качестве упражнения для читателя!

Заметил, что в IOS7 sizeToFit также не работает - возможно, решение может помочь вам

[textView sizeToFit];
[textView layoutIfNeeded];

Еще одна опция, обеспечивающая синхронизацию labelMaxLayoutWidth с шириной метки:

#import "MyLabel.h"

@implementation MyLabel

-(void)setBounds:(CGRect)bounds
{
    [super setBounds:bounds];

    // This appears to be needed for iOS 6 which doesn't seem to keep
    // label preferredMaxLayoutWidth in sync with its width, which 
    // means the label won't grow vertically to encompass its text if 
    // the label's width constraint changes.
    self.preferredMaxLayoutWidth = self.bounds.size.width;
}

@end

Я чувствую, что должен внести свой вклад, поскольку мне потребовалось некоторое время, чтобы найти правильное решение:

  • Цель состоит в том, чтобы позволить Auto Layout выполнять свою работу, даже не вызывая sizeToFit(), мы сделаем это, указав правильные ограничения:
  • Укажите верхний, нижний и начальный / конечный пробелы на вашей UILabel
  • Установите для свойства количество строк значение 0
  • Увеличьте приоритет содержания контента до 1000
  • Понизьте приоритет сопротивления сжатию содержимого до 500
  • На нижнем ограничении контейнера уменьшите приоритет до 500

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

Я добавил UILabel программно и в моем случае этого было достаточно:

label.translatesAutoresizingMaskIntoConstraints = false
label.setContentCompressionResistancePriority(UILayoutPriorityRequired, forAxis: .Vertical)
label.numberOfLines = 0

В моем случае я создавал подкласс UIView, который содержал UILabel (неизвестной длины). В iOS7 код был прост: устанавливайте ограничения, не беспокойтесь об объятии контента или сопротивлении сжатию, и все работало как ожидалось.

Но в iOS6 UILabel всегда была обрезана до одной строки. Ни один из ответов выше не работал для меня. Настройки содержания и сопротивления сжатию игнорировались. Единственное решение, которое предотвратило обрезку, заключалось в том, чтобы на метке было указано предпочтение MaxLayoutWidth. Но я не знал, как установить предпочтительную ширину, так как размер его родительского представления был неизвестен (действительно, это будет определяться содержимым).

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

- (void)layoutSubviews
{
    // Autolayout hack required for iOS6
    [super layoutSubviews];
    self.bodyLabel.preferredMaxLayoutWidth = self.bodyLabel.frame.size.width;
    [super layoutSubviews];
}

Я решил с помощью xCode6 установить "Предпочитаемую ширину" в режим "Автоматически" и закрепить метку сверху, впереди и сзади.

UIFont *customFont = myLabel.font;
CGSize size = [trackerStr sizeWithFont:customFont
                             constrainedToSize:myLabel.frame.size // the size here should be the maximum size you want give to the label
                                 lineBreakMode:UILineBreakModeWordWrap];
float numberOfLines = size.height / customFont.lineHeight;
myLabel.numberOfLines = numberOfLines;
myLabel.frame = CGRectMake(258, 18, 224, (numberOfLines * customFont.lineHeight));

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

В любом случае, для меня сработало добавление требуемого ограничения высоты для UILabel и установка его вручную на правильную высоту при вызове intrinsicContentSize. Если у вас нет UILabel, содержащегося в другом UIView, вы можете попробовать создать подкласс UILabel и предоставить аналогичную реализацию, сначала установив ограничение высоты, а затем вернув
[super instrinsicContentSize]; вместо [self.containerview intrinsiceContentSize]; как я делаю ниже, что специфично для моего подкласса UIView.

- (CGSize)intrinsicContentSize
{
    CGRect expectedTextBounds = [self.titleLabel textRectForBounds:self.titleLabel.bounds limitedToNumberOfLines:1];
    self.titleLabelHeightConstraint.constant = expectedTextBounds.size.height;
    return [self.containerView intrinsicContentSize];

}

Отлично работает сейчас на iOS 7 и iOS 8.

Решение, которое сработало для меня; Если ваша UILabel имеет фиксированную ширину, измените ограничение с constant = в constant <= в вашем файле интерфейса

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

В моем случае при использовании меток в UITableViewCell метка at изменится, но высота превысит высоту ячейки таблицы. Это то, что сработало для меня. Я сделал в соответствии с Максом Маклаудом, а затем убедился, что высота ячейки была установлена ​​на UITableViewAutomaticDimension.

Вы можете добавить это в вашем init или awakeFromNib,

self.tableView.rowHeight = UITableViewAutomaticDimension.  

В раскадровке выберите ячейку, откройте инспектор размера и убедитесь, что для высоты строки установлено значение "По умолчанию", сняв флажок "Пользовательский".

В 8.0 есть проблема, которая также требует, чтобы это было установлено в коде.

Другие вопросы по тегам