SetNeedsLayout против setNeedsUpdateConstraints и layoutIfNeeded vs updateConstraintsIfNeeded

Я знаю, что цепь автоматического макета состоит в основном из трех разных процессов.

  • ограничения обновления
  • макеты (здесь мы получаем расчет кадров)
  • дисплей

То, что не совсем ясно для меня, - это внутреннее различие между -setNeedsLayout и -setNeedsUpdateConstraints. От Apple Docs:

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

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

Ответ 1

Ваши выводы правы. Основная схема:

  • setNeedsUpdateConstraints гарантирует, что последующий вызов updateConstraintsIfNeeded вызывает updateConstraints.
  • setNeedsLayout гарантирует, что будущий вызов layoutIfNeeded вызывает layoutSubviews.

Когда вызывается layoutSubviews, он также вызывает updateConstraintsIfNeeded, поэтому вызов его вручную редко требуется в моем опыте. На самом деле, я никогда не вызывал его, кроме как при отладке макетов.

Обновление ограничений с помощью setNeedsUpdateConstraints тоже довольно редко, objc.io-a должен читать об автозапутах - говорит:

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

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

Основные правила:

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

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

Ответ 2

Ответ на обратную связь довольно правильный. Однако я хотел бы добавить некоторые дополнительные сведения.

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

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

updateConstraints обычно ничего не делает. Он просто решает ограничения, которые он не применяет, до тех пор, пока не будет вызван layoutSubviews. Поэтому для анимации требуется вызов layoutSubviews.

  1. setNeedsLayout вызывает также метод -updateContraints

Нет, это не обязательно. Если ваши ограничения не были изменены, UIView пропустит вызов до updateConstraints. Вам нужно явно вызвать setNeedsUpdateConstraint, чтобы изменить ограничения в этом процессе.

Для вызова updateConstraints вам необходимо сделать следующее:

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