Когда layoutSubviews называется?
у меня есть пользовательский вид, который не получает layoutSubview
сообщения во время анимации.
у меня есть вид, который заполняет экран. В нижней части экрана есть настраиваемое подвиде, которое правильно изменяется в Interface Builder, если я изменяю высоту панели навигации. layoutSubviews
вызывается при создании представления, но больше никогда. Мои подвиды правильно изложены. Если я отключу строку состояния в вызове, подвида layoutSubviews
не вызывается вообще, хотя основной вид анимируется его размер.
при каких обстоятельствах layoutSubviews
на самом деле называется?
Я autoresizesSubviews
значение NO
для моего пользовательского представления. И в Interface Builder у меня есть верхняя и нижняя стойки и набор вертикальных стрелок.
10 ответов
у меня был аналогичный вопрос, но не был удовлетворен ответом (или любым, который я мог найти в сети), поэтому я попробовал его на практике, и вот что я получил:
-
init
не вызываетlayoutSubviews
to называться (duh) -
addSubview:
причиныlayoutSubviews
быть вызванным на вид добавляется, вид это добавлено в (целевой вид), и все подвиды цели - посмотреть
setFrame
интеллигентно зоветlayoutSubviews
на вид, имеющий свой набор кадров только если параметр size кадра разные - прокрутка UIScrollView
причины
layoutSubviews
для вызова scrollView и его супервизор - поворот всего устройства звонки
layoutSubview
на родительский вид (на первичный ответ 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
метод объекта базового слоя представления.
некоторые из пунктов в ответ BadPirate верны только частично:
-
на
addSubView
точкаaddSubview
вызывает layoutSubviews для добавляемого представления, представления, в которое оно добавляется (целевое представление), и всех подвидов цели.это зависит от вида (целевой вид) autoresize маска. Если он имеет маску autoresize, layoutSubview будет вызываться на каждом
addSubview
. Если у него нет маска autoresize затем 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, выделил объект Window, в инспекторе выбрал вкладку window Attributes (крайняя слева) и проверил "видимый при запуске". Кажется, что в SDK 3 он все еще использовался для вызова layoutSubViews, но не в 4.
6 часов разочарования положить конец.
другая часть головоломки заключается в том, что окно должно быть сделано ключом:
[window makeKeyAndVisible];
of 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))