Centrar una vista en su supervista usando Visual Format Language


160

Recién comencé a aprender AutoLayout para iOS y eché un vistazo al lenguaje de formato visual.

Todo funciona bien, excepto por una cosa: simplemente no puedo obtener una vista para centrar dentro de su supervista.
¿Es esto posible con VFL o necesito crear manualmente una restricción yo mismo?

Respuestas:


232

Actualmente, no, no parece que sea posible centrar una vista en la supervista usando solo VFL. Sin embargo, no es tan difícil hacerlo usando una sola cadena VFL y una sola restricción adicional (por eje):

VFL: "|-(>=20)-[view]-(>=20)-|"

[NSLayoutConstraint constraintWithItem:view
                             attribute:NSLayoutAttributeCenterX
                             relatedBy:NSLayoutRelationEqual
                                toItem:view.superview
                             attribute:NSLayoutAttributeCenterX
                            multiplier:1.f constant:0.f];

Uno podría pensar que simplemente sería capaz de hacer esto (que es lo que inicialmente pensé e intenté cuando vi esta pregunta):

[NSLayoutConstraint constraintsWithVisualFormat:@"|-(>=20)-[view(==200)]-(>=20)-|"
                                 options: NSLayoutFormatAlignAllCenterX | NSLayoutFormatAlignAllCenterY
                                 metrics:nil
                                   views:@{@"view" : view}];

Intenté muchas variaciones diferentes de lo anterior tratando de doblarlo a mi voluntad, pero esto no parece aplicarse a la supervista incluso cuando explícitamente tiene dos cadenas VFL separadas para ambos ejes ( H:|V:). Entonces empecé a tratar de aislar exactamente cuando las opciones no obtener aplicado a la VFL. Parecen no aplicarse a la supervista en el VFL y solo se aplicarán a las vistas explícitas que se mencionan en la cadena de VFL (lo que es decepcionante en ciertos casos).

Espero que en el futuro Apple agregue algún tipo de nueva opción para que las opciones de VFL tengan en cuenta la supervista, incluso si lo hacen solo cuando solo hay una vista explícita aparte de la supervista en la VFL. Otra solución podría ser otra opción pasada en el VFL que dice algo como: NSLayoutFormatOptionIncludeSuperview.

No hace falta decir que aprendí mucho sobre VFL tratando de responder esta pregunta.


2
Gracias por esa respuesta detallada. Sin embargo, la única razón para usar VFL es deshacerme de esos feos trozos de restricciones. Lástima que Apple todavía no sea compatible con esto ...
Christian Schnorr

@larsacus ¿Podría comentar por qué funciona la respuesta a continuación? Me tiene perplejo.
Matt G

8
La respuesta de Evgeny a continuación funciona porque el motor de distribución automática trata |y [superview]como entidades internas separadas a pesar de que representan el mismo objeto semántico. Al usar [superview], la distribución automática incluye el objeto de supervista en sus métricas de alineación (en el caso de su respuesta en el enlace github, es la NSLayoutFormatAlignAllCenterY/ Xmétrica).
larsacus

@larsacus ¡Genial, gracias!
Matt G

¿Sabemos si la limitación aún se aplica con la versión de VFL que se incluye con iOS 7.x actual?
Drux

138

Sí, es posible centrar una vista en su supervista con Visual Format Language. Tanto vertical como horizontalmente. Aquí está la demostración:

https://github.com/evgenyneu/center-vfl


15
Esto es increíble, ¿crees que podrías ampliar esto y explicar por qué funciona?
tapi

3
Terminé utilizando algo más cercano a la respuesta marcada, pero +1 para un archivo adjunto de github en el suyo
Rembrandt Q. Einstein

44
@Dan Si no funciona para usted, es posible que también necesite tener restricciones de ancho y alto. El VFL sería así para el ancho: @ "[etiqueta (== 200)]" y así para la altura: @ "V: [etiqueta (== 200)]"
Jonathan Zhan

1
¿Por qué los resultados de | y [superview] diferente? ¿Es esto algo que debería ser rastreado o está sucediendo algo que simplemente no estoy siguiendo?
Matt G

3
No se puede analizar el formato de restricción: las opciones enmascaran las vistas que se deben alinear en un borde horizontal, lo que no está permitido para el diseño que también es horizontal. H: [supervista] - (<= 1) - [etiqueta1]
grabner

82

Creo que es mejor crear restricciones manualmente.

[superview addConstraint:
 [NSLayoutConstraint constraintWithItem:view
                              attribute:NSLayoutAttributeCenterX
                              relatedBy:NSLayoutRelationEqual
                                 toItem:superview
                              attribute:NSLayoutAttributeCenterX
                             multiplier:1
                               constant:0]];
[superview addConstraint:
 [NSLayoutConstraint constraintWithItem:view
                              attribute:NSLayoutAttributeCenterY
                              relatedBy:NSLayoutRelationEqual
                                 toItem:superview
                              attribute:NSLayoutAttributeCenterY
                             multiplier:1
                               constant:0]];

Ahora podemos usar NSLayoutAnchor para definir mediante programación AutoLayout :

(Disponible en iOS 9.0 y posterior)

view.centerXAnchor.constraint(equalTo: superview.centerXAnchor).isActive = true
view.centerYAnchor.constraint(equalTo: superview.centerYAnchor).isActive = true

Recomiendo usar SnapKit , que es un DSL para facilitar el diseño automático en iOS y macOS.

view.snp.makeConstraints({ $0.center.equalToSuperview() })

1
Funcionó mejor en mi contexto, aunque no hay VFL.
brainray

me da una advertencia:Warning once only: Detected a case where constraints ambiguously suggest a height of zero for a tableview cell's content view. We're considering the collapse unintentional and using standard height instead.
Tapas Pal

10

Absolutamente posible pude hacerlo así:

[NSLayoutConstraint constraintsWithVisualFormat:@"H:[view]-(<=1)-[subview]"
                                        options:NSLayoutFormatAlignAllCenterY
                                        metrics:nil
                                          views:NSDictionaryOfVariableBindings(view, subview)];

Aprendí esto de aquí. El código anterior centra la subvista a la vista por Y obviamente.

Swift3:

        NSLayoutConstraint.constraints(withVisualFormat: "H:[view]-(<=1)-[subview]",
                                       options: .alignAllCenterY,
                                                       metrics: nil,
                                                       views: ["view":self, "subview":_spinnerView])

7

Agregue el siguiente código y tenga su botón en el centro de la pantalla. Absolutamente posible

[self.view addConstraints:[NSLayoutConstraint
                           constraintsWithVisualFormat:@"H:|-[button]-|"
                           options:NSLayoutFormatAlignAllCenterY
                           metrics:0
                           views:views]];

[self.view addConstraints:[NSLayoutConstraint
                           constraintsWithVisualFormat:@"V:|-[button]-|"
                           options:NSLayoutFormatAlignAllCenterX
                           metrics:0
                           views:views]];

1

Sé que no es lo que quieres pero, por supuesto, puedes calcular los márgenes y usarlos para crear la cadena de formato visual;)

De todos modos, no. Desafortunadamente, no es posible hacer eso 'automáticamente' con VFL, al menos todavía no.


Bueno, puedes usar NSString stringWithFormat: para construir tu cadena VFL dinámicamente en tiempo de ejecución.
TRVD1707

1

Gracias a la respuesta de @Evgenii, creo un ejemplo completo en gist:

centre el cuadrado rojo verticalmente con una altura personalizada

https://gist.github.com/yallenh/9fc2104b719742ae51384ed95dcaf626

Puede centrar una vista verticalmente usando VFL (que no es del todo intuitivo) o usar restricciones manualmente. Por cierto, no puedo combinar el centro y la altura juntos en VFL como:

"H:[subView]-(<=1)-[followIcon(==customHeight)]"

En la solución actual, las restricciones de VFL se agregan por separado.


0

Lo que hay que hacer es que la supervista se declare en el diccionario. En lugar de usarlo |usas @{@"super": view.superview};.

Y NSLayoutFormatAlignAllCenterXpara vertical y NSLayoutFormatAlignAllCenterYpara horizontal como opciones.


0

Puedes usar vistas adicionales.

NSDictionary *formats =
@{
  @"H:|[centerXView]|": @0,
  @"V:|[centerYView]|": @0,
  @"H:[centerYView(0)]-(>=0)-[yourView]": @(NSLayoutFormatAlignAllCenterY),
  @"V:[centerXView(0)]-(>=0)-[yourView]": @(NSLayoutFormatAlignAllCenterX),
};
NSDictionary *views = @{
  @"centerXView": centerXView, 
  @"centerYView": centerYView, 
  @"yourView": yourView,
};
 ^(NSString *format, NSNumber *options, BOOL *stop) {
     [superview addConstraints:
      [NSLayoutConstraint constraintsWithVisualFormat:format
                                              options:options.integerValue
                                              metrics:nil
                                                views:views]];
 }];

Si lo quieres ordenado, entonces centerXView.hidden = centerYView.hidden = YES;.

PD: No estoy seguro de poder llamar a las vistas adicionales "marcadores de posición", porque el inglés es mi segundo idioma. Será apreciado si alguien me puede decir eso.


Algo falta aquí. ¿Cómo se llama al bloque bajo la declaración de vistas ? No es un código legal de la forma en que lo pones
ishahak

0

Para aquellos que vinieron aquí para una Interface Buildersolución basada (Google me lleva aquí), simplemente agregue restricciones de ancho / alto constante, y seleccione la subvista -> arrastre de control a su supervista -> seleccione "centrar vertical / horizontalmente en la supervista".


0

Este es mi metodo

//create a 100*100 rect in the center of superView
var consts = NSLayoutConstraint.constraints(withVisualFormat: "H:|-space-[myView]-space-|", options: [], metrics:["space":view.bounds.width/2-50], views:["myView":myView])

consts += NSLayoutConstraint.constraints(withVisualFormat: "V:|-space-[myView]-space-|", options: [], metrics: ["space":view.bounds.height/2-50], views: ["myView":myView])

-1

Solo necesito decir que puedes usar esto en lugar de restricciones:

child.center = [parent convertPoint:parent.center fromView:parent.superview];

44
Sólo hay que decir, que no es lo que está pidiendo ni va a funcionar si se gira el dispositivo, de jugar a pantalla cambia de tamaño, etc.
jeffjv

-1

Respuesta de @ igor-muzyka en Swift 2 :

NSLayoutConstraint.constraintsWithVisualFormat("H:[view]-(<=1)-[subview]", 
                                               options: .AlignAllCenterY,
                                               metrics: nil,
                                               views: ["view":view, "subview":subview])

-3

En lugar de usar VFL, puede hacerlo fácilmente en el generador de interfaces. En el Storyboard principal, ctrl + clic en la vista que desea centrar. Arrastre a la supervista (mientras mantiene presionada la tecla Ctrl) y seleccione "Centro X" o "Centro Y". No se necesita código.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.