setNeedsLayout vs. setNeedsUpdateConstraints and layoutIfNeeded vs updateConstraintsIfNeeded


227

Я знаю, що ланцюжок автоматичного компонування складається в основному з 3 різних процесів.

  1. оновлення обмежень
  2. перегляди макета (тут ми отримуємо розрахунок кадрів)
  3. показ

Що для мене не зовсім зрозуміло - це внутрішня різниця між -setNeedsLayoutта -setNeedsUpdateConstraints. З Документів Apple:

setNeedsLayout

Викличте цей метод у головному потоці програми, коли ви хочете налаштувати макет підпідзору подання. Цей метод записує запит і повертається негайно. Оскільки цей метод не примушує негайного оновлення, а замість цього чекає наступного циклу оновлення, ви можете використовувати його, щоб визнати недійсним макет кількох представлень даних до того, як будь-який із цих представлень буде оновлений. Така поведінка дозволяє консолідувати всі оновлення макета в один цикл оновлення, що зазвичай краще для продуктивності.

setNeedsUpdateConstraints

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

Коли я хочу анімувати подання після зміни обмеження та оживити зміни, які, як правило, закликаю, наприклад:

[UIView animateWithDuration:1.0f delay:0.0f usingSpringWithDamping:0.5f initialSpringVelocity:1 options:UIViewAnimationOptionCurveEaseInOut animations:^{
        [self.modifConstrView setNeedsUpdateConstraints];
        [self.modifConstrView layoutIfNeeded];
    } completion:NULL];

Я виявив, що якщо я використовую -setNeedsLayoutзамість -setNeedsUpdateConstraintsвсе працювати , як очікувалося, але якщо я міняю -layoutIfNeededз -updateConstraintsIfNeeded, анімація не буде.
Я намагався зробити власний висновок:

  • -updateConstraintsIfNeeded лише обмеження щодо оновлення, але не змушує макет вступати в процес, тому оригінальні кадри все ще зберігаються
  • -setNeedsLayout-updateContraintsметод також викликає

Отже, коли нормально використовувати один замість іншого? а щодо методів компонування, чи потрібно мені викликати їх у поданні, яке має зміну обмеження або на батьківському поданні?


27
Я не розумію, що люди зводяться ... Так що вам слід щось з цим зробити, як-от запитати причину як обов'язкову або вони абсолютно безглузді
Андреа

7
Можливо, їм просто потрібно отримати значок критики (перший голос "за")
fujianjin6471,

1
Дуже рекомендую вам побачити тут . Відповідь - це більше вирішення реальної проблеми. Дивіться також це відео
Мед

Відповіді:


258

Ваші висновки правильні. Основна схема:

  • setNeedsUpdateConstraintsзабезпечує наступний дзвінок на updateConstraintsIfNeededдзвінки updateConstraints.
  • setNeedsLayoutзабезпечує наступний дзвінок на layoutIfNeededдзвінки layoutSubviews.

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

Оновлення обмежень, використовуючи setNeedsUpdateConstraintsтакож досить рідко, Objc.io – обов'язково читати про автовиведення – каже :

Якщо згодом щось зміниться, що скасовує одне з ваших обмежень, вам слід негайно усунути обмеження і викликати setNeedsUpdateConstraints. Насправді, це єдиний випадок, коли вам доведеться викликати пропуск оновлення обмежень.

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

Основні правила:

  • Якщо ви маніпулювали обмеженнями безпосередньо, зателефонуйте setNeedsLayout.
  • Якщо ви змінили деякі умови (на зразок компенсації чи smth), які змінили б обмеження у вашому переосмисленому updateConstraintsметоді (рекомендований спосіб зміни обмежень, btw), дзвінок setNeedsUpdateConstraintsі більшу частину часу setNeedsLayoutпісля цього.
  • Якщо вам потрібні будь-які дії, зазначені вище, негайного ефекту - наприклад, коли вам потрібно дізнатися нову висоту кадру після проходження макета - додайте його з a layoutIfNeeded.

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


@coverback, так objc.io каже: "Якщо щось пізніше зміниться, що призведе до недійсності одного з ваших обмежень, вам слід негайно усунути обмеження і викликати setNeedsUpdateConstraints. Насправді, це єдиний випадок, коли вам доведеться запустити пропуск оновлення обмежень". А потім у блоці Animation сказано, що коли я видаляю, додаю або змінюю constraint.contant, я повинен викликати setNeedsLayout. Яка різниця? Я відчуваю себе справді дурним :(
pash3r

3
@ pash3r Відмінність в оновленні константа не кваліфікується як "недійсна". Недійсність - це коли вона взагалі не актуальна, як, наприклад, має бути приєднана до іншого виду або видалена повністю. Констант просто розміщував би погляд ближче чи далі, або міняв би його розмір, таким чином, потреба setNeedsLayout.
обкладинка

@coverback setNeedsLayoutгарантує, що layoutSubviewsбуде викликано наступний цикл оновлення, але, можливо, це нічого спільного не має layoutIfNeeded?
fujianjin6471

2
@coverback Якщо ви маніпулюєте обмеженнями безпосередньо, вам layoutSubviewsбуде викликано автоматично, не потрібно дзвонитиsetNeedsLayout
fujianjin6471

Так, маніпулювання властивостями обмеження безпосередньо призведе до запуску layoutSubviews, тому не потрібно робити це вручну. Однак layoutIfNeededвам потрібно зателефонувати, якщо вам потрібні зміни негайно, а не наступний макет циклу
Чарлі Мартін,

89

Відповідь на coverback досить правильно. Однак я хотів би додати деякі додаткові деталі.

Нижче наведена схема типового циклу UIView, що пояснює інші форми поведінки:

Життєвий цикл UIView

  1. Я виявив, що якщо я використовую -setNeedsLayoutзамість -setNeedsUpdateConstraintsвсе працювати , як очікувалося, але якщо я міняю -layoutIfNeededз -updateConstraintsIfNeeded, анімація не буде.

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

  1. setNeedsLayout викликає також метод -updateContraints

Ні в цьому немає необхідності. Якщо ваші обмеження не були змінені, UIView пропустить дзвінок на updateConstraints. Вам потрібно чітко зателефонувати, setNeedsUpdateConstraintщоб змінити обмеження в процесі.

Для дзвінка updateConstraintsвам потрібно зробити наступне:

[view setNeedsUpdateConstraints];
[view setNeedsLayout]; 
[view layoutIfNeeded];

Дякую, це вирішило моє питання. У мене було UIWindow без батьківського UIView, у якому до нього додавалися тимчасові обмеження після виклику LayoutIfNeeded () перед анімацією. Додавання обгортки субпрограми до вікна UIW і виклик цих трьох методів у ньому вирішило мою проблему.
masterwok

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