2009-04-08 11 views
212

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

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

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

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

ответ

6

Я отследил решение до настойчивости Interface Builder, что пружины не могут быть изменены в представлении с включенными имитируемыми элементами экрана (строка состояния и т. Д.). Поскольку пружины были отключены для основного вида, этот вид не мог изменить размер и, следовательно, был прокручен в полном объеме, когда появилась панель вызова.

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

Дополнительная проблема при отладке заключается в том, что симулятор покидает приложение, когда состояние вызова в режиме вызова переключается через меню. Выйти из приложения = нет отладчика.

+0

Вы говорите, что layoutSubviews вызывается, когда представление изменяет размер? Я всегда предполагал, что это не так ... –

+0

Это так. Когда размер изображения изменяется, ему нужно что-то делать с его подзонами. Если вы не предоставите его, он автоматически перемещает их, используя пружины, стойки и т. Д. –

4

Вы посмотрели на layoutIfNeeded?

Ниже приведен фрагмент документации. Работает ли анимация, если вы вызываете этот метод явно во время анимации?

layoutIfNeeded Оставляет подзоны при необходимости.

- (void)layoutIfNeeded 

Обсуждение Используйте этот метод, чтобы заставить макет подобозрения перед нанесением.

Доступность: Доступен в iPhone OS 2.0 и новее.

0

Другая часть головоломки в том, что окно должно быть ключ:

[window makeKeyAndVisible]; 

из еще на подвиды автоматически не изменяется.

2

При переносе приложения OpenGL из SDK 3 в 4, layoutSubviews больше не вызывался. После большого количества проб и ошибок я, наконец, открыл MainWindow.xib, выбрал объект Window, в инспекторе выбрал вкладку «Атрибуты окна» (слева) и отметит «Visible at launch». Похоже, что в SDK 3 он все еще использовался для вызова layoutSubViews, но не в 4.

6 часов разочарования положил конец.

+0

Вы сделали ключ окна? Если нет, это может привести к тому, что всевозможные интересные вещи не произойдут. –

415

У меня был подобный вопрос, но не был удовлетворен ответом (или я мог бы найти в интернете), так что я попробовал его на практике, и вот что я получил:

  • init делает не вызывает layoutSubviews к называться (Дух)
  • addSubview: вызывает layoutSubviews называться на зрения добавляемого мнение, что это существо добавлено к (целевой точки зрения), и все подвидов из мишени
  • вид setFrame разумно называет layoutSubviews на вид того, его рама установлена ​​только , если параметр размер кадра отличается
  • прокрутки в UIScrollView вызывает layoutSubviews называться на в Scrollview, и его SuperView
  • вращающегося устройства вызывает только layoutSubview на родительский виде (The реагирующего viewControllers первичной вид)
  • Изменение размера вид будет вызывать layoutSubviews на его надтаблицы

Мои результаты - http://blog.logichigh.com/2011/03/16/when-does-layoutsubviews-get-called/

+1

Отличный ответ. Я всегда задавался вопросом о 'layoutSubviews'. «InitWithFrame:' ​​вызывает 'layoutSubviews' для вызова? – Robert

+2

@Robert - Я использовал initWithFrame ... так нет. – BadPirate

+0

Я столкнулся с ситуациями, когда setFrame меняет размер, но не вызывает вызовы layoutSubviews. Понятия не имею почему. См. Этот вопрос: http://stackoverflow.com/questions/7921610/why-might-layoutsubviews-not-automatically-be-called-on-a-view-when-its-frame-is –

9

Некоторые из точек BadPirate's answer являются лишь отчасти:

  1. Для addSubView точки

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

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

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

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

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

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

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

53

Основываясь на предыдущем ответе от @BadPirate, я немного экспериментировал и придумал некоторые пояснения/исправления. Я обнаружил, что layoutSubviews: будет вызван тем, если и только если:

  • свои собственные границы (не кадр) изменились.
  • Оценки одного из его прямых подзонов изменены.
  • Подвью добавляется в представление или удаляется из представления.

Некоторые соответствующие детали:

  • Границы считаются изменены только, если новое значение отличается, в том числе различного происхождения. Обратите внимание, именно поэтому layoutSubviews: вызывается всякий раз, когда прокручивается UIScrollView, поскольку он выполняет прокрутку, изменяя начало своих границ.
  • Изменение фрейма изменит границы только в том случае, если размер был изменен, и это единственное, что в любом случае распространяется на свойство bounds.
  • Изменение границ представления, которое еще не находится в иерархии вида, приведет к вызову layoutSubviews:, когда представление в конечном итоге будет добавлено в иерархию вида.
  • И только для полноты: эти триггеры не напрямую call layoutSubviews, а скорее звоните setNeedsLayout, который устанавливает/поднимает флаг. Каждая итерация цикла запуска для всех просмотров в иерархии представлений, этот флаг отмечен. Для каждого вида, где обнаружен флаг, на него вызывается layoutSubviews:, и флаг сбрасывается. Сначала будут проверены/вызваны виды выше иерархии.
+4

не может выдвинуть этот ответ достаточно. это должен быть главный ответ. три приведенных правила - это все, что вам нужно. я никогда не сталкивался с каким-либо поведением layoutSubview, которое эти правила не описывали отлично. –

6

вызова [self.view setNeedsLayout]; в ViewController делает его называть viewDidLayoutSubviews

6

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

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

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

+0

Также важно отметить, что ни одно из этих событий не вызывается, когда представление еще не добавлено в стек представления. Вы включаете это со словом «can», но, в частности, он вызывается в следующем доступном цикле основного потока приложения, когда он был отмечен как необходимый макет одним из этих событий. – user1122069

0

Другой довольно туманна, но очень важный случай, когда не layoutSubviews никогда не вызывается является:

class View: UIView { 

    override class var layerClass: AnyClass { return Layer.self } 

    class Layer: CALayer { 
     override func layoutSublayers() { 
      // if we don't call super.layoutSublayers() 
     } 
    } 

    override func layoutSubviews() { // this never gets called by the OS! 
     print(#function) 
    } 
}