ios cocoa-touch autolayout bounds layoutsubviews

ios - ¿Cuándo se llama layoutSubviews?



cocoa-touch autolayout (10)

¿has mirado layoutIfNeeded?

El fragmento de documentación está abajo. ¿Funciona la animación si llamas a este método explícitamente durante la animación?

layoutIfNeeded Establece las subvistas si es necesario.

- (void)layoutIfNeeded

Discusión Use este método para forzar el diseño de subvistas antes de dibujar.

Disponibilidad disponible en iPhone OS 2.0 y versiones posteriores.

Tengo una vista personalizada que no layoutSubview mensajes de vista de vista de layoutSubview durante la animación.

Tengo una vista que llena la pantalla. Tiene una subvista personalizada en la parte inferior de la pantalla que cambia de tamaño correctamente en Interface Builder si cambio la altura de la barra de navegación. Se llama a layoutSubviews cuando se crea la vista, pero nunca más. Mis subvistas están correctamente presentadas. Si desactivo la barra de estado durante la llamada, no se llama en absoluto a layoutSubviews la subvista, aunque la vista principal sí anima su cambio de tamaño.

¿Bajo qué circunstancias se llama layoutSubviews ?

Tengo autoresizesSubviews configuradas en NO para mi vista personalizada. Y en Interface Builder tengo los puntales superior e inferior y el conjunto de flechas verticales.


Al migrar una aplicación OpenGL de SDK 3 a 4, ya no se llamaba a layoutSubviews. Después de muchas pruebas y errores, finalmente abrí MainWindow.xib, seleccioné el objeto Ventana, en el inspector, seleccioné la pestaña Atributos de la Ventana (más a la izquierda) y verifiqué "Visible at launch". Parece que en el SDK 3 todavía se utiliza para provocar una llamada layoutSubViews, pero no en 4.

6 horas de frustración acabadas.


Algunos de los puntos en la respuesta de BadPirate son solo parcialmente ciertos:

  1. Para el punto addSubView

    addSubview hace que se llame a layoutSubviews en la vista que se agrega, la vista a la que se agrega (vista de destino) y todas las subvistas del destino.

    Depende de la máscara de tamaño automático de la vista (vista de destino). Si tiene la máscara de tamaño automático activada, se abrirá layoutSubview en cada addSubview . Si no tiene una máscara de tamaño automático, se llamará a layoutSubview solo cuando cambie el tamaño del marco de la vista (vista de destino).

    Ejemplo: si creó UIView mediante programación (no tiene una máscara de tamaño automático de forma predeterminada), LayoutSubview se llamará solo cuando el marco UIView no cambie en cada addSubview .

    Es a través de esta técnica que el rendimiento de la aplicación también aumenta.

  2. Para el punto de rotación del dispositivo.

    Girar un dispositivo solo llama a layoutSubview en la vista principal (la vista primaria del controlador de vista que responde)

    Esto puede ser cierto solo cuando su VC está en la jerarquía de VC (raíz en window.rootViewController ), bueno, este es el caso más común. En iOS 5, si creas un VC, pero no se agrega a ningún otro VC, entonces este VC no se notará cuando el dispositivo gire. Por lo tanto, su vista no se notaría llamando a layoutSubviews.


Otra parte del rompecabezas es que la ventana debe ser clave:

[window makeKeyAndVisible];

de lo contrario las subvistas no se redimensionan automáticamente.


Rastreé la solución hasta la insistencia de Interface Builder de que los resortes no se pueden cambiar en una vista que tenga activados los elementos simulados de la pantalla (barra de estado, etc.). Como los resortes estaban desactivados para la vista principal, esa vista no podía cambiar de tamaño y, por lo tanto, se desplazó hacia abajo en su totalidad cuando apareció la barra de llamada entrante.

Desactivar las funciones simuladas, cambiar el tamaño de la vista y configurar los resortes correctamente hizo que se produjera la animación y que se llamara a mi método.

Un problema adicional al depurar esto es que el simulador abandona la aplicación cuando el estado de la llamada se alterna a través del menú. Salir de la aplicación = sin depurador.


Sobre la base de la respuesta anterior de @BadPirate, experimenté un poco más y encontré algunas aclaraciones / correcciones. Encontré que layoutSubviews: se llamará en una vista si y solo si:

  • Sus propios límites (no marco) cambian.
  • Los límites de una de sus subvistas directas cambiaron.
  • Se agrega una subvista a la vista o se elimina de la vista.

Algunos detalles relevantes:

  • Los límites se consideran modificados solo si el nuevo valor es diferente, incluido un origen diferente . Tenga en cuenta específicamente que ese es el motivo layoutSubviews: se llama a layoutSubviews: cada vez que un UIScrollView se desplaza, ya que realiza el desplazamiento al cambiar el origen de sus límites.
  • Cambiar el marco solo cambiará los límites si el tamaño ha cambiado, ya que esto es lo único propagado a la propiedad de los límites.
  • Un cambio en los límites de una vista que aún no se encuentra en una jerarquía de vistas resultará en una llamada a layoutSubviews: cuando la vista finalmente se agregue a una jerarquía de vistas .
  • Y solo para completar: estos activadores no llaman directamente layoutSubviews, sino que llaman a setNeedsLayout , que establece / levanta una bandera. Cada iteración del bucle de ejecución, para todas las vistas en la jerarquía de vistas , se marca esta bandera. Para cada vista donde se encuentra levantada la bandera, layoutSubviews: se llama en ella y la bandera se reinicia. Las vistas más arriba en la jerarquía serán revisadas / llamadas primero.

Tuve una pregunta similar, pero no estaba satisfecho con la respuesta (o alguna que pudiera encontrar en la red), así que la probé en la práctica y esto es lo que obtuve:

  • init no hace que se layoutSubviews a layoutSubviews (duh)
  • addSubview: hace que se layoutSubviews a layoutSubviews en la vista que se agrega, la vista a la que se agrega (vista de destino) y todas las subvistas del destino
  • view setFrame llama inteligentemente layoutSubviews en la vista que tiene su marco establecido solo si el parámetro de tamaño del marco es diferente
  • el desplazamiento de un UIScrollView hace que se layoutSubviews a layoutSubviews en el scrollView, y su superview
  • la rotación de un dispositivo solo llama a layoutSubview en la vista principal (la vista principal de los viewControllers que responden)
  • Cambiar el tamaño de una vista llamará layoutSubviews en su supervisión

Mis resultados - http://blog.logichigh.com/2011/03/16/when-does-layoutsubviews-get-called/


Un caso bastante oscuro, pero potencialmente importante cuando nunca se llama layoutSubviews es:

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))


llamando a [self.view setNeedsLayout]; in viewController lo hace para llamar a viewDidLayoutSubviews


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

Los cambios de diseño pueden ocurrir cuando ocurre cualquiera de los siguientes eventos en una vista:

a. El tamaño del rectángulo de una vista cambia.
segundo. Se produce un cambio de orientación de la interfaz, que generalmente activa un cambio en el rectángulo de los límites de la vista raíz.
do. El conjunto de subcapas Core Animation asociadas con la capa de la vista cambia y requiere diseño.
re. Su aplicación hace que el diseño se produzca llamando al método setNeedsLayout o layoutIfNeeded de una vista.
mi. Su aplicación fuerza el diseño llamando al método setNeedsLayout del objeto de capa subyacente de la vista.