Когда вызывается layoutSubviews? - PullRequest
254 голосов
/ 08 апреля 2009

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

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

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

У меня autoresizesSubviews установлено на NO для моего пользовательского просмотра. А в Интерфейсном Разработчике у меня есть верхняя и нижняя распорки и вертикальная стрелка.


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

[window makeKeyAndVisible];

, иначе подпредставления не изменяются автоматически.

Ответы [ 9 ]

466 голосов
/ 16 марта 2011

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

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

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

72 голосов
/ 21 октября 2013

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

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

Некоторые важные сведения:

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

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

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

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

9 голосов
/ 29 октября 2012

Некоторые из пунктов Ответ BadPirate верны лишь частично:

  1. Для addSubView Точка

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

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

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

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

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

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

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

7 голосов
/ 15 февраля 2014

вызывающий [self.view setNeedsLayout]; в viewController делает это, чтобы вызвать viewDidLayoutSubviews

7 голосов
/ 09 апреля 2009

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

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

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

4 голосов
/ 08 апреля 2009

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

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

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

- (void)layoutIfNeeded

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

Доступность Доступно в iPhone OS 2.0 и более поздних версиях.

2 голосов
/ 24 июня 2010

При переносе приложения OpenGL из SDK 3 в 4 layoutSubviews больше не вызывался. После долгих проб и ошибок я наконец открыл MainWindow.xib, выбрал объект Window, в инспекторе выбрал вкладку Window Attributes (крайний левый) и установил флажок «Видимо при запуске». Кажется, что в SDK 3 он по-прежнему вызывал вызов layoutSubViews, но не в 4.

6 часов разочарования подошли к концу.

0 голосов
/ 26 июля 2017

Довольно неясный, но потенциально важный случай, когда 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))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...