Ответ 1
Layout
Предположим, вы инкапсулируете свою логику вида в подкласс UIView
и называете ее SomeView
. Это означает, что SomeView
должен знать, как сама компоновка, то есть, как размещать в ней некоторые другие представления (вы также можете создать представление, которое рисуется без использования каких-либо подзаголовков, но это выходит за рамки потребностей среднего разработчика).
Этот макет выполняется [SomeView layoutSubviews]
. У вас есть возможность переопределить его:
subview.frame = CGRectMake(100 + shiftX, 50 + shiftY, 250 + shiftX - paddingRight, ...
// Oh no, I think I didn't do it right.
но вам редко приходится это делать. В темные времена Cocoa Touch этот макет был широко распространен, но теперь я бы сказал, что 99% макетов можно покрыть с помощью автоматической компоновки.
Система должна знать, когда она должна звонить [UIView layoutSubviews]
. Очевидно, это было сделано в первый раз, когда вам нужно нарисовать представление, но оно также может быть вызвано всякий раз, когда изменяется кадр надзора. Вот подробное объяснение.
Поэтому система часто вызывает [view layoutIfNeeded]
. Вы также можете вызвать его в любое время, но это будет действовать только в том случае, если есть какое-то событие, которое вызвало [view setNeedsLayout]
, или если вы вызвали его вручную, как в этом случае.
Ограничения
Автоматическая компоновка (капитализированная таким образом в документация) называется так, потому что вы уходите [SomeView layoutSubviews]
, поскольку он унаследован от UIView
и
вместо этого укажите положение ваших подзонов в терминах ограничений.
При использовании автоматического макета система будет выполнять вызовы [view updateConstraintsIfNeeded]
на каждом макете. Однако, только если установлен флаг [view setNeedsUpdateConstraints];
, метод вызывает -updateConstraints
(который выполняет реальную работу).
Если вы не используете автоматический макет, эти методы не имеют отношения.
Вы можете реализовать его как в этом примере.
Ваш пример
Редко бывает необходимо напрямую вызвать -layoutIfNeeded
и -updateConstraintsIfNeeded
, потому что движок UI будет делать это автоматически при каждом проходе макета. Однако в этом случае автор решил называть их немедленно; это потому, что результирующая высота нужна прямо сейчас, а не в какой-то момент в будущем.
Этот метод обновления высоты ячейки кажется правильным. Обратите внимание, что cell
может быть вновь созданной ячейкой и, таким образом, не добавлен в иерархию представлений; это не влияет на его способность компоновки.
Заключение
В вашем пользовательском представлении перейдите со следующими параметрами, начиная с самого "универсального" и заканчивая большинством "настраиваемых":
- Создание ограничений при создании вида (вручную или в IB)
- Если вам нужно позже изменить ограничения, переопределите
-updateConstraints
. - Если у вас сложный макет, который не может быть описан выше, переопределите
-layoutSubviews
.
В коде, который изменяет то, что может изменить ваши ограничения, вызовите
[view setNeedsUpdateConstraints];
Если вам нужны результаты сразу, вызовите также
[view updateConstraintsIfNeeded];
Если в кадре изменения кода используется
[view setNeedsLayout];
и, наконец, если вы хотите получить результаты сразу, вызовите
[view layoutIfNeeded];
Вот почему все четыре вызова необходимы в этом случае.
Дополнительные материалы
Взгляните на подробное объяснение в статье Расширенные средства автоматической компоновки, objc.io issue # 3