UILabel sizeToFit не працює з автоматичним вимиканням ios6


152

Як я повинен програмно налаштувати (і в якому методі) UILabel, висота якого залежить від його тексту? Я намагався налаштувати його за допомогою комбінації дошки та коду, але безрезультатно. Усі рекомендують sizeToFitпід час налаштування lineBreakModeта numberOfLines. Однак, незалежно від того , якщо я ставлю цей код в viewDidLoad:, viewDidAppear:або viewDidLayoutSubviewsя не можу отримати його на роботу. Або я роблю поле занадто малим для довгого тексту, і він не зростає, або я занадто великий, і він не скорочується.


FWIW той самий код: мені не потрібно було використовувати label.sizeToFit()в Xcode / viewController, обмежень було достатньо. не створили етикетку на Playground . Поки єдиний спосіб, коли я виявив, що це працює на Playground, це зробитиlabel.sizeToFit()
Honey

Відповіді:


407

Зверніть увагу, що в більшості випадків рішення Метта працює як очікувалося. Але якщо це не допоможе вам, будь ласка, читайте далі.

Щоб автоматично міняти висоту мітки, потрібно зробити наступне:

  1. Встановити обмеження для компонування для мітки
  2. Встановіть обмеження висоти з низьким пріоритетом. Він повинен бути нижчим за ContentCompressionResistancePriority
  3. Встановити числоOfLines = 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_)]];

Використання інтерфейсу

  1. Встановіть чотири обмеження. Обмеження висоти є обов'язковим. введіть тут опис зображення

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

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

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

  5. І зменшити пріоритет обмеження висоти.
    введіть тут опис зображення

Насолоджуйтесь. :)


4
ТАК! Це саме та відповідь, яку я шукав. Єдине, що мені потрібно було зробити - це встановити contentHuggingPriority та contentCompressionResistancePriority на необхідне. Я міг би зробити це все в IB! Дуже дякую.
schelego

43
Ти це передумуєш. Встановіть preferredMaxLayoutWidthабо закріпіть ширину мітки (або її правою та лівою сторонами). Це все! Потім він автоматично зростатиме і скорочується вертикально, щоб відповідати його вмісту.
мат

Мені довелося встановити перевагу властивостіMaxLayoutWidth + прикріпити ширину мітки. Працює як шарм :)
MartinMoizard

переважнаMaxLayoutWidth та / або закріплення лівої та правої сторін не є абсолютною. Пріоритети обіймання та стиснення вмісту завжди працюватимуть до тих пір, поки їх пріоритети будуть вищими, ніж інші обмеження.
Ерік Алфорд

2
^ І я нарешті з’ясував, чому, Можливо, варто згадати, коли у вас клітина прилипає до нижньої частини перегляду, вам потрібно встановити пріоритет обмеження "донизу" менше ніж 1000, я ставлю його на 750 і все працює ідеально. Я здогадуюсь це зменшило погляд.
Mathijs Segers

65

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

У більш складних випадках просто встановіть мітки preferredMaxLayoutWidth.

У будь-якому випадку правильна справа відбувається автоматично.


12
Вам також потрібно переконатися, що встановіть числоOfLines на 0, інакше ви не отримаєте обгортання.
devongovett

2
Цього мені було недостатньо. Також потрібен пункт 2 з відповіді @ Марка.
Данял Айтекін

чи можливо також зробити так, щоб мітка зростала автоматично на кілька рядків без написання кодів?
Noor

Це набагато менш складно, ніж прийнята відповідь. Я зробив два приклади ( тут і тут ), що ілюструють цю відповідь.
Сурагч

@Suragch Використання обмежень це працює. Але це не працює для одного і того ж коду в Playground . На дитячому майданчику ви повинні матиlabel.sizeToFit()
Мед

42

Хоча питання задається програмно, зіткнувшись з тією ж проблемою і вважаючи за краще працювати в Interface Builder, я подумав, що може бути корисним доповнити існуючі відповіді рішенням Interface Builder.

Перше, що потрібно забути sizeToFit. Автоматичний макет буде вирішувати це від вашого імені залежно від внутрішнього розміру вмісту.

Тому проблема полягає в тому, як отримати етикетку, яка відповідає її вмісту за допомогою автоматичного макета? Конкретніше - тому що в цьому питанні згадується - висота. Зауважте, що ті самі принципи застосовуються і до ширини.

Отже, почнемо з прикладу UILabel, висота якого встановлена ​​у висоту 41 пікселів:

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

Як ви бачите на захопленні екрана вгорі, "This is my text"має підкладки зверху і знизу. Це прокладка між висотою UILabel, і це вміст, текст.

Якщо ми запускаємо додаток у тренажері, то, звичайно, ми бачимо те саме:

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

Тепер виберемо UILabel в Interface Builder і подивимось параметри за замовчуванням в інспекторі розмірів:

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

Зверніть увагу на виділене вище обмеження. Це пріоритет наповнення змісту . Оскільки Еріка Садун описує це у чудовому автоматичному макеті iOS, демістифікованому , це:

спосіб перегляду воліє уникати зайвих оббивок навколо його основного вмісту

Для нас, з UILabel, основним змістом є текст.

Ось ми і в основі цього основного сценарію. Ми надали нашій текстовій мітці два обмеження. Вони конфліктують. Один каже, що "висота повинна бути дорівнює 41 пікселям" . Інший каже: "обійняти погляд на його вміст, щоб у нас не було зайвих накладок" . У нашому випадку обійміть погляд на текст, щоб у нас не було зайвих накладок.

Тепер, з автоматичним макетом, з двома різними інструкціями, які говорять робити різні речі, час виконання повинен вибрати одну чи іншу. Це не може зробити і те, і інше. Висота UILabel не може бути високою як 41 піксель, так і не має прокладок.

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

Тож давайте давай. Моє обмеження висоти має пріоритет 1000 , який необхідний . Висота вмісту обіймів - 250 , що є слабким . Що станеться, якщо зменшити пріоритет обмеження висоти до 249 ?

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

Тепер ми можемо побачити, що магія починає відбуватися. Спробуємо в сім:

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

Дивовижно! Досягнуте змісту обіймання. Тільки тому, що пріоритет висоти 249 менший, ніж пріоритет 250, який охоплює вміст . В основному я кажу, що "висота, яку я тут вказую, менш важлива, ніж те, що я вказав для обіймів вмісту" . Отже, зміст обіймів виграє.

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

Залишимо робити еквівалент по ширині як вправу для читача!


3
Дякую за велике пояснення! Нарешті я розумію пріоритет обіймання вмісту.
kernix

Поясніть, будь ласка, також, що є пріоритетом стійкості до стиснення вмісту? Дякую!
kernix

2
Відмінник відповіді Макс! Чи можливо дещо обмежити кількість ліній, що працюють таким чином?
rafaeljuzo

7

Помічений у розмірі IOS7 розмірToFit також не працював - можливо, рішення може допомогти і вам

[textView sizeToFit];
[textView layoutIfNeeded];

1
@Rambatino Це працює для мене. Додайте, layoutIfNeededколи вам потрібно setNeedsUpdateConstraints. Але ситуація може бути дещо іншою.
Gon

Це найкраще працювало для мене, коли я робив поповер. Мені потрібно було зробити макетIfNeeded першим, однак
Джейсон

4

Інший варіант забезпечення бажаної міткиMaxLayoutWidth мітки зберігається в синхронізованому з шириною мітки:

#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

4

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

  • Мета полягає в тому, щоб дозволити автоматичній розкладці виконувати свою роботу, не викликаючи ніколи sizeToFit (), ми зробимо це, вказавши правильні обмеження:
  • Вкажіть верхній, нижній та провідний / кінцеві обмеження місця на своєму UILabel
  • Встановіть кількість властивостей рядків на 0
  • Збільште пріоритет обіймання вмісту до 1000
  • Знизіть пріоритет стійкості до стиснення вмісту до 500
  • На нижньому обмеженні контейнера знизьте пріоритет до 500

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


3

У моєму випадку я створював підклас 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];
}

Це був саме такий сценарій, який у мене був… іноді приємно знати, що ти не один.
daveMac

Адам, я борюся з тим, що ти сказав, працював ідеально в ios7. У мене є власна клітина з uiview та uilabel. Я можу отримати етикетку відповідно до вмісту, але перегляд не робить, незалежно від обмежень, які я накладаю на нього. Як ти змусив це працювати? Клянусь, я спробував все, але це не забирає висоту етикетки
denikov

3

Я додав UILabelпрограмно, і в моєму випадку цього було достатньо:

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

2

Я вирішив за допомогою xCode6 поставити "бажану ширину" в автоматичну та закріпити верхню частину етикетки, провідну та кінцеву введіть тут опис зображення


0
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));

0

Я зіткнувся з цією проблемою також з підкласом 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.


Варто зауважити, що якщо ви правильно налаштували свій погляд (тобто ваші обмеження є правильними, і ви все налаштували під час правильних етапів у життєвому циклі подання), вам ніколи не доведеться переосмислювати intrinsicContentSize. Час виконання повинен бути в змозі обчислити це виходячи з ваших правильних обмежень налаштування.
Джон Роджерс

Теоретично це повинно бути правильним, але ми не маємо доступу до всіх приватних API та реалізацій Apple, і вони не є непогрішними та мають помилки у своєму коді.
n8tr

... але [super intrinsicContentSize] повинен подбати про це, це те, що я говорю. Якщо ви правильно реалізували автоматичний макет
Джон Роджерс

0

Рішення, яке працювало для мене; Якщо ваш UILabel має фіксовану ширину, змініть обмеження constant =на constant <=на файл інтерфейсу

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


-1

У моєму випадку при використанні міток у UITableViewCell мітка при розмірі буде розміром, але висота перевищить висоту комірки таблиці. Це те, що працювало для мене. Я зробив це відповідно до Макса Маклеода, а потім переконався, що висота комірки встановлена ​​на UITableViewAutomaticDimension.

Ви можете додати це у своєму init або wakefromNromb,

self.tableView.rowHeight = UITableViewAutomaticDimension.  

У дошці розкадрування виберіть комірку, відкрийте інспектор розміру та переконайтесь, що для висоти рядка встановлено значення "За замовчуванням", знявши прапорець "Користувацький".

В 8.0 є проблема, яка також вимагає встановлення її в коді.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.