¿Dónde debería establecer restricciones de diseño automático al crear vistas mediante programación?

79

Veo diferentes ejemplos donde se establecen restricciones. Algunos los establecen en viewDidLoad/ loadView(después de que se agregó la subvista). Otros los configuran en el método updateViewConstraints, que es llamado por viewDidAppear.

Cuando intento establecer restricciones, updateViewContraintspuede haber un salto en el diseño, por ejemplo, un ligero retraso antes de que aparezca la vista. Además, si utilizo este método, ¿debo eliminar primero las restricciones existentes, es decir [self.view [removeConstraints:self.view.constraints]?

Heisenberg
fuente
1
Tuve la misma experiencia con updateViewConstraints, así que dejé de intentar usarlo. Configuro restricciones en viewDidLoad o en el método updateConstraints de una vista personalizada. Con suerte, alguien te dará una respuesta definitiva.
bilobatum
1
updateViewConstraints: Puede anular este método en una subclase para agregar restricciones a la vista o sus subvistas. (de los documentos de Apple )
prueba el

Respuestas:

108

Configuré mis restricciones en viewDidLoad/ loadView(me dirijo a iOS> = 6). updateViewConstraintses útil para cambiar los valores de las restricciones, por ejemplo, si alguna restricción depende de la orientación de la pantalla (lo sé, es una mala práctica), puede cambiarla constanten este método.

La adición de restricciones viewDidLoadse muestra durante la sesión "Introducción al diseño automático para iOS y OS X" (WWDC 2012), a partir de las 39:22. Creo que es una de esas cosas que se dicen durante las conferencias pero que no llegan a la documentación.

ACTUALIZACIÓN: He notado la mención de configurar restricciones en la Gestión de recursos en los Controladores de vista :

Si prefiere crear vistas mediante programación, en lugar de utilizar un guión gráfico, puede hacerlo anulando el loadView método del controlador de vista . Su implementación de este método debería hacer lo siguiente:

(...)

3.Si está utilizando el diseño automático, asigne suficientes restricciones a cada una de las vistas que acaba de crear para controlar la posición y el tamaño de sus vistas . De lo contrario, implemente los métodos viewWillLayoutSubviewsy viewDidLayoutSubviewspara ajustar los marcos de las subvistas en la jerarquía de vistas. Consulte "Cambiar el tamaño de las vistas del controlador de vista".

ACTUALIZACIÓN 2 : Durante la WWDC 2015 de Apple dio una nueva explicación de updateConstraintsy updateViewConstraintsuso recomendado:

Realmente, todo esto es una forma de que las vistas tengan la oportunidad de realizar cambios en las restricciones justo a tiempo para la siguiente pasada de diseño, pero a menudo no es realmente necesario.

Idealmente, toda su configuración inicial de restricciones debería ocurrir dentro de Interface Builder.

O si realmente encuentra que necesita asignar sus restricciones programáticamente, algún lugar como viewDidLoad es mucho mejor.

Las restricciones de actualización son realmente solo para el trabajo que debe repetirse periódicamente.

Además, es bastante sencillo simplemente cambiar las restricciones cuando crea la necesidad de hacerlo; mientras que, si separa esa lógica del otro código que está relacionado con ella y la mueve a un método separado que se ejecuta en un momento posterior, su código se vuelve mucho más difícil de seguir, por lo que será más difícil para usted mantener , será mucho más difícil de entender para otras personas.

Entonces, ¿cuándo necesitaría usar restricciones de actualización?

Bueno, todo se reduce al rendimiento.

Si encuentra que simplemente cambiar sus restricciones en su lugar es demasiado lento, entonces las restricciones de actualización podrían ayudarlo.

Resulta que cambiar una restricción dentro de las restricciones de actualización es en realidad más rápido que cambiar una restricción en otros momentos.

La razón de esto es que el motor puede tratar todos los cambios de restricción que ocurren en esta pasada como un lote.

Arek Holko
fuente
2
+1 para esto como la mejor respuesta. Apple prescribe loadView como el lugar correcto para establecer las restricciones iniciales y esto no requiere un indicador BOOL adicional en el método updateConstraints (que parece un truco).
awolf
4
En mi opinión, la vista debería ser responsable de las restricciones, no el controlador de vista. En muchos casos, el controlador de vista ni siquiera sabe cuáles son todos los elementos de la vista (por ejemplo, etiquetas estáticas en una celda de vista de tabla).
dasdom
1
@dasdom ¿Cómo puede la vista controlar su relación con otras vistas? Tienes que establecer restricciones como @"|-[button1]-[button2]-|"en ViewController, ¿verdad? ¿O hay una forma diferente?
Joseph
@Casper La mayoría de las veces tengo una subclase UIView, que es la vista del controlador de vista. Dentro de esa vista hay subvistas y las restricciones para las subvistas.
dasdom
3
¿Por qué es una mala práctica cambiar las restricciones updateViewConstraints?
prueba el
33

Recomiendo crear un BOOL y configurarlo en -updateConstraintsUIView (o -updateViewConstraints, para UIViewController).

-[UIView updateConstraints]: (documentos de Apple)

Las vistas personalizadas que establecen restricciones por sí mismas deben hacerlo anulando este método.

Ambos -updateConstraintsy -updateViewConstraintsse pueden llamar varias veces durante la vida de una vista. (Llamar setNeedsUpdateConstraintsa una vista provocará que esto suceda, por ejemplo). Como resultado, debe asegurarse de evitar la creación y activación de restricciones duplicadas, ya sea usando un BOOL para realizar una configuración de restricción determinada solo una vez, o asegurándose de para desactivar / eliminar restricciones existentes antes de crear y activar nuevas.

Por ejemplo:

  - (void)updateConstraints {  // for view controllers, use -updateViewConstraints

         if (!_hasLoadedConstraints) {
              _hasLoadedConstraints = YES;
             // create your constraints
         }
         [super updateConstraints];
    }

Saludos a @fresidue en los comentarios por señalar que los documentos de Apple recomiendan llamar supercomo último paso. Si llama superantes de realizar cambios en algunas restricciones, puede encontrar una excepción en tiempo de ejecución (bloqueo).

BooRanger
fuente
7
No estoy seguro de si hace alguna diferencia pragmáticamente, pero la documentación dice "Importante: Llame a [super updateConstraints] como paso final en su implementación".
Fresidue
De acuerdo con los documentos, si cambia una vista en tiempo de ejecución e invalida sus restricciones, el sistema elimina inmediatamente esa restricción y llama a setNeedsUpdateConstraints. Antes de realizar un nuevo diseño, el sistema llama a updateConstraints, donde puede personalizar el diseño invalidado. Como tal, probablemente no establecería una bandera en este método que podría evitar que el sistema lo llame.
smileBot
1
@cocoanutmobile Si usa una marca BOOL como se sugiere en esta respuesta, es solo para evitar que algunas de sus restricciones se agreguen más de una vez. Es algo perfectamente bueno para hacer. Otra alternativa es simplemente almacenar una referencia a cualquier restricción que cree y luego asegurarse de eliminar (desactivar) todas antes de crear y activar las nuevas. Sin embargo, este enfoque producirá un peor rendimiento, especialmente si sus restricciones antiguas y nuevas son exactamente las mismas.
smileyborg
2
@cocoanutmobile Además, tenga en cuenta que la bandera BOOL aquí no impide que el sistema llame -updateConstraintsni nada, todavía se llamaría y usted todavía llama [super updateConstraints].
smileyborg
1
Como mencionó @fresidue, asegúrese de llamar superal final de su implementación de -updateConstraintso -updateViewConstraints. Consulte este comentario para obtener más información.
smileyborg
4

Esto debe hacerse en ViewDidLoad, según el video WWDC de Apple y la documentación.

No tengo idea de por qué la gente recomienda updateConstraints. Si lo hace en updateConstraints, tendrá problemas con NSAutoresizingMaskLayoutConstraint con cambio de tamaño automático porque sus vistas ya han tenido en cuenta las máscaras automáticas. Debería eliminarlos en updateConstraints para que funcione.

UpdateConstraints debería ser solo para eso, cuando necesite 'actualizarlos', hacer cambios, etc.desde su configuración inicial.

BluelineCodificación
fuente
1
¿Puede agregar los enlaces para el video y la documentación a la que se refiere aquí?
Shivam Pokhriyal
2

Hágalo a la vista hizo el método de subvistas de diseño

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
}
Equipo
fuente
1

Tengo esta solución para cambiar las restricciones antes de que se carguen los que están en el guión gráfico. Esta solución elimina los retrasos después de que se carga la vista.

-(void)updateViewConstraints{

    dispatch_async(dispatch_get_main_queue(), ^{

            //Modify here your Constraint -> Activate the new constraint and deactivate the old one

            self.yourContraintA.active = true;
            self.yourContraintB.active= false;
            //ecc..
           });

    [super updateViewConstraints]; // This must be the last thing that you do here -> if! ->Crash!
}
Davide
fuente
1

También puede configurarlos en viewWillLayoutSubviews :

 override func viewWillLayoutSubviews() {

    if(!wasViewLoaded){
        wasViewLoaded = true

        //update constraint

        //also maybe add a subview            
    }
}
Andre Simon
fuente
0

Esto funcionó para mí:

Rápido 4.2

override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

// Modify your constraints in here

  ...

}

Aunque sinceramente no estoy seguro de que valga la pena. Parece un poco más lento de cargar que en viewDidLoad (). Solo quería sacarlos de este último, porque se está volviendo masivo.

Michele Dall'Agata
fuente
0

El siguiente ejemplo es pasar cualquier vista a otra clase. crear mi vista desde el guión gráfico

Swift 5.0

    override func viewWillAppear(_ animated: Bool) {
        
      super.viewWillAppear(animated) 
        DispatchQueue.main.async {
            self.abcInstance = ABC(frame: self.myView.frame)
          } 
      }

 

Si pierde DispatchQueue.main.async, llevará tiempo actualizar las restricciones en viewWillAppear. Cree myView en el guión gráfico y dé las mismas restricciones que el ancho y la altura de la pantalla, luego intente imprimir el marco de myView. dará un valor exacto en DispatchQueue.main.async o en viewDidAppear pero no dará un valor exacto en viewWillAppear sin DispatchQueue.main.async.

Shrikant Phadke
fuente