Когда layoutSubviews называется?
у меня есть пользовательский вид, который не получает layoutSubview
сообщения во время анимации.
у меня есть вид, который заполняет экран. Он имеет пользовательский подвида в нижней части экрана, который правильно изменяет размер в Interface Builder, если я изменить высоту панели навигации. layoutSubviews
вызывается при создании представления, но больше никогда. Мои подвиды правильно выложены. Если я отключу строку состояния in-call, подвида layoutSubviews
не вызывается вообще, даже если главное представление анимируется его размер.
при каких обстоятельствах layoutSubviews
на самом деле называется?
Я autoresizesSubviews
значение NO
для моего пользовательского представления. И в Interface Builder у меня есть верхняя и нижняя стойки и набор вертикальных стрелок.
10 ответов:
у меня был аналогичный вопрос, но я не был удовлетворен ответом (или любым, который я мог найти в сети), поэтому я попробовал его на практике, и вот что я получил:
init
не вызываетlayoutSubviews
to называется (дух)addSubview:
причиныlayoutSubviews
для вызова на вид добавляется, вид это быть добавлено (целевое представление), и все подвиды цели- view
setFrame
интеллигентно зоветlayoutSubviews
на вид, имеющий свой набор рамок только если параметр размера рамы разные- прокрутка UIScrollView причины
layoutSubviews
для вызова в представление ScrollView, и его суперпанель- вращение устройства вызывает только вызовы
layoutSubview
на родительском представлении (the отвечая viewControllers первичный view)- изменение размера представления вызовет
layoutSubviews
на его суперпанельмои результаты - http://blog.logichigh.com/2011/03/16/when-does-layoutsubviews-get-called/
основываясь на предыдущем ответе @BadPirate, я немного поэкспериментировал и придумал некоторые разъяснения/исправления. Я нашел это
layoutSubviews:
будет вызываться на представление тогда и только тогда, когда:
- своя границы (не рамы) изменен.
- границы одного из его прямых подвидов изменились.
- вложенное представление добавляется в представление или удаляется из представления.
некоторые важные детали:
- в границы считаются измененными только в том случае, если новое значение отличается,в том числе различного происхождения. Обратите внимание, именно поэтому
layoutSubviews:
вызывается всякий раз, когда прокручивается UIScrollView, поскольку он выполняет прокрутку, изменяя происхождение своих границ.- изменение кадра изменит только границы, если размер изменился, так как это единственное, что распространяется на свойство bounds.
- изменение границ представления, которое еще не находится в иерархии представлений будет результат в вызове
layoutSubviews:
когда представление в конечном итоге добавляется в иерархию представлений.- и просто для полноты: эти триггеры не напрямую вызов layoutSubviews, а скорее вызов
setNeedsLayout
, который устанавливает/поднимает флаг. Каждая итерация цикла выполнения, для всех представлений в иерархии представлений этот флаг. Для каждого вида, где флаг найден поднятым,layoutSubviews:
вызывается на нем и флаг сбрасывается. Взгляды выше вверх иерархия будет проверена / вызвана в первую очередь.
изменения макета могут происходить всякий раз, когда происходит любое из следующих событий в виде:
a. размер прямоугольника границ представления изменяется.
b. происходит изменение ориентации интерфейса, которое обычно вызывает изменение границ корневого представления прямоугольник.
c. набор основных анимационных подслоев, связанных со слоем представления, изменяется и требует компоновки.
d. ваше приложение заставляет макет происходить путем вызоваsetNeedsLayout
илиlayoutIfNeeded
метод просмотра.
e. ваше приложение заставляет макет, вызываяsetNeedsLayout
метод объекта базового слоя представления.
некоторые пункты в ответ Бадпирата верны только частично:
на
addSubView
точка
addSubview
вызывает layoutSubviews для вызова добавляемого вида, вида, в который он добавляется (целевой вид), и всех подвидов целевого объекта.это зависит от вида (целевой вид) autoresize маски. Если он имеет автоматического изменения размеров маски, layoutSubview будет вызываться на каждом
addSubview
. Если у него нет автоматического изменения размеров маски layoutSubview будет называться только тогда, когда представления (целевое представление) рамка изменения размера.пример: если вы создали UIView программно (по умолчанию он не имеет маски авторазбора), LayoutSubview будет вызываться только тогда, когда фрейм UIView изменяется не на каждом
addSubview
.именно благодаря этому методу производительность приложения также увеличивается.
для точки вращения устройства
поворот устройство вызывает layoutSubview только в Родительском представлении (основное представление отвечающего viewController)
это может быть верно только тогда, когда ваш VC находится в иерархии VC (root at
window.rootViewController
), ну это самый распространенный случай. В iOS 5, Если вы создаете VC, но он не добавляется ни в один другой VC, то этот VC не будет замечен при вращении устройства. Поэтому его вид не будет замечен путем вызова layoutSubviews.
я отследил решение до настойчивого требования Interface Builder о том, что пружины не могут быть изменены в представлении, в котором включены имитируемые элементы экрана (строка состояния и т. д.). Поскольку пружины были выключены для основного вида, этот вид не мог изменить размер и, следовательно, был прокручен вниз полностью, когда появилась панель вызова.
выключение моделируемых объектов, затем изменение размера представления и правильная настройка пружин привели к возникновению анимации и моему методу называемый.
дополнительная проблема при отладке заключается в том, что симулятор выходит из приложения, когда статус вызова переключается через меню. Выйти из приложения = нет отладчика.
вы смотрели layoutIfNeeded?
фрагмент документации приведен ниже. Работает ли анимация, если вы вызываете этот метод явно во время анимации?
layoutIfNeeded При необходимости выкладывает подвиды.
- (void)layoutIfNeeded
Обсуждение Этот метод используется для принудительной компоновки подвидов перед рисованием.
в наличии Доступно в iPhone OS 2.0 и более поздних версиях.
при переносе приложения OpenGL из SDK 3 в 4 layoutSubviews больше не вызывался. После многих проб и ошибок я, наконец, открыл MainWindow.xib, выделил объект окна, в инспекторе выбрал вкладку атрибуты окна (самая левая) и поставил галочку "видимый при запуске". Кажется, что в SDK 3 он все еще используется для вызова layoutSubViews, но не в 4.
6 часов разочарования положить конец.
другая часть головоломки заключается в том, что окно должно быть сделано ключом:
[window makeKeyAndVisible];
из else вложенные представления не изменяются автоматически.
довольно неясный, но потенциально важный случай, когда
layoutSubviews
никогда не называли это:import UIKit class View: UIView { override class var layerClass: AnyClass { return Layer.self } class Layer: CALayer { override func layoutSublayers() { // if we don't call super.layoutSublayers()... print(type(of: self), #function) } } override func layoutSubviews() { // ... this method never gets called by the OS! print(type(of: self), #function) } } let view = View(frame: CGRect(x: 0, y: 0, width: 100, height: 100))