Когда layoutSubviews называется?


у меня есть пользовательский вид, который не получает layoutSubview сообщения во время анимации.

у меня есть вид, который заполняет экран. Он имеет пользовательский подвида в нижней части экрана, который правильно изменяет размер в Interface Builder, если я изменить высоту панели навигации. layoutSubviews вызывается при создании представления, но больше никогда. Мои подвиды правильно выложены. Если я отключу строку состояния in-call, подвида layoutSubviews не вызывается вообще, даже если главное представление анимируется его размер.

при каких обстоятельствах layoutSubviews на самом деле называется?

Я autoresizesSubviews значение NO для моего пользовательского представления. И в Interface Builder у меня есть верхняя и нижняя стойки и набор вертикальных стрелок.

10 230

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: вызывается на нем и флаг сбрасывается. Взгляды выше вверх иерархия будет проверена / вызвана в первую очередь.

https://developer.apple.com/library/prerelease/tvos/documentation/WindowsViews/Conceptual/ViewPG_iPhoneOS/CreatingViews/CreatingViews.html#//apple_ref/doc/uid/TP40009503-CH5-SW1

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

a. размер прямоугольника границ представления изменяется.
b. происходит изменение ориентации интерфейса, которое обычно вызывает изменение границ корневого представления прямоугольник.
c. набор основных анимационных подслоев, связанных со слоем представления, изменяется и требует компоновки.
d. ваше приложение заставляет макет происходить путем вызова setNeedsLayout или layoutIfNeeded метод просмотра.
e. ваше приложение заставляет макет, вызывая setNeedsLayout метод объекта базового слоя представления.

некоторые пункты в ответ Бадпирата верны только частично:

  1. на addSubView точка

    addSubview вызывает layoutSubviews для вызова добавляемого вида, вида, в который он добавляется (целевой вид), и всех подвидов целевого объекта.

    это зависит от вида (целевой вид) autoresize маски. Если он имеет автоматического изменения размеров маски, layoutSubview будет вызываться на каждом addSubview. Если у него нет автоматического изменения размеров маски layoutSubview будет называться только тогда, когда представления (целевое представление) рамка изменения размера.

    пример: если вы создали UIView программно (по умолчанию он не имеет маски авторазбора), LayoutSubview будет вызываться только тогда, когда фрейм UIView изменяется не на каждом addSubview.

    именно благодаря этому методу производительность приложения также увеличивается.

  2. для точки вращения устройства

    поворот устройство вызывает layoutSubview только в Родительском представлении (основное представление отвечающего viewController)

    это может быть верно только тогда, когда ваш VC находится в иерархии VC (root at window.rootViewController), ну это самый распространенный случай. В iOS 5, Если вы создаете VC, но он не добавляется ни в один другой VC, то этот VC не будет замечен при вращении устройства. Поэтому его вид не будет замечен путем вызова layoutSubviews.

вызов [self.view setNeedsLayout]; в viewController делает это для вызова viewDidLayoutSubviews

я отследил решение до настойчивого требования 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))