Resortes en diseño automático: Distribuya vistas de manera uniforme, con restricciones, en Xcode 5

Entiendo el antiguo método de Struts and Springs para alinear, dimensionar y distribuir vistas en Interface Builder. Sin embargo, parece que no puedo encontrar la forma de distribuir las vistas de forma uniforme usando el diseño automático con Xcode 5. Había una manera de hacerlo con Xcode 4, pero esa opción ya no está.

Tengo 7 botones dispuestos en una stack vertical. En un diseño de 3.5 “, se ve genial. Cuando obtengo una vista previa de la pantalla en el diseño de 4”, todos los botones permanecen bien empaquetados y hay una gran cantidad de espacio debajo del último botón.

Quiero que se mantengan a la misma altura, pero quiero que el espacio entre ellos sea flexible para que puedan extenderse por la pantalla.

enter image description here

Pude flexionar la altura de los botones y llenar el espacio, pero ese no es mi comportamiento deseado. Me gustaría aprender a usar el diseño automático para reemplazar mi antiguo comportamiento de resortes, pero parece que no puedo encontrar la forma de hacerlo a través de Interface Builder.

Estoy de acuerdo con que el botón superior sea un espacio fijo desde el borde superior o un espacio proporcional desde el borde superior, del mismo modo para el botón inferior y el borde inferior. Esos son menos importantes para mí, estoy bien con ambos.

Pero realmente necesito encontrar la manera de distribuir el espacio extra entre cada uno de los elementos de la vista.

EDITAR Tenga en cuenta que en iOS 9 esta técnica será innecesaria, ya que UIStackView realizará la distribución automáticamente. Agregaré otra respuesta explicando cómo funciona eso.

Cómo realizar una distribución pareja con Autolayout

La forma más sencilla de hacerlo en Interface Builder solo (en lugar de construir restricciones en el código) es usar vistas “espaciadoras”:

  1. Coloque los botones superior e inferior absolutamente.

  2. Coloque vistas de espaciador entre todos los botones. Use restricciones para ubicarlos horizontalmente (centrarlos horizontalmente es lo más simple) y establecer sus anchos.

  3. Haga restricciones entre cada botón y la vista del espaciador arriba y abajo, con una Constante de 0.

  4. Ahora seleccione todas las vistas espaciadoras y establezca sus alturas para que sean iguales.

La primera captura de pantalla me muestra configurando esto en IB:

enter image description here

No he corregido deliberadamente las “vistas fuera de lugar” porque quiero que veas cómo se ve mientras estoy diseñando las restricciones. Aquí está el resultado en una pantalla de 4 pulgadas y una de 3.5 pulgadas:

enter image description here

He dejado las vistas espaciadoras negras, solo para mostrarte cómo funciona esta técnica, pero por supuesto en la vida real las harías transparentes y por lo tanto invisibles. De modo que el usuario ve solo sus botones, distribuidos uniformemente en cualquier altura de la pantalla.

La razón del uso de esta técnica es que, aunque la noción de igualdad realiza la distribución de los valores que está solicitando, las restricciones pueden aplicar la igualdad solo entre los aspectos de las vistas; por lo tanto, necesitamos las vistas adicionales (las vistas espaciadoras) para que tengamos cosas que podamos igualar a otras cosas (aquí, las alturas de las vistas espaciadoras).

Otros enfoques

Obviamente, un enfoque más flexible es asignar las restricciones en el código. Esto puede sonar desalentador, pero hay muchos códigos de terceros para ayudarlo, como este tipo de cosas .

Por ejemplo, si tenemos una supervista (posiblemente invisible) cuya altura actúa como un límite para dictar la distribución vertical máxima de nuestros cuatro botones, podemos fijar sus partes superiores al centro vertical de esa supervista con una constant de 0 pero un multiplier de 0.000001 , 0.666667 , 1.33333 y 2.0 respectivamente (si tenemos cuatro botones); ahora los botones se mantendrán verticalmente distribuidos incluso cuando la supervista cambie de tamaño en respuesta a la altura de la pantalla o lo que sea. [En Xcode 5.1, será posible configurarlo en Interface Builder, pero en versiones anteriores de Xcode no es posible.]

En iOS 9 / Xcode 7, este problema se resolverá trivialmente en IB. Simplemente seleccione los botones (o lo que sea que desee distribuir verticalmente) y elija Editor> Incrustar en> Vista de stack. Luego simplemente configura la vista de la stack:

  • Proporcione restricciones que posicionen y dimensionen la vista de la stack en sí. Por ejemplo, fija los cuatro bordes de la vista de stack a los cuatro bordes de su supervista.

  • Establezca los atributos de la vista de stack. En este caso, queremos eje vertical, alineación de relleno, distribución de espaciado igual.

¡Eso es todo! Sin embargo, puede tener curiosidad acerca de cómo funciona esto, porque aún es posible hacer lo mismo manualmente en el código. Una vista de stack realiza la distribución, no insertando vistas espaciadoras, sino insertando guías espaciadoras. Una guía (una UILayoutGuide) es un objeto liviano que se comporta como una vista para propósitos de restricciones de diseño, pero no es una vista y, por lo tanto, no tiene que hacerse invisible y no carga ninguno de los gastos generales de una vista.

Para ilustrar, haré en código lo que está haciendo la vista de stack. Supongamos que tenemos cuatro vistas para distribuir verticalmente. Les asignamos restricciones para todo excepto su distribución:

  • Todos ellos tienen restricciones absolutas de altura

  • Su izquierda está anclado a la izquierda de la supervista, y su derecho está anclado a la derecha de la superconvista

  • La parte superior de la vista superior está fijada a la parte superior de la supervista, y la parte inferior de la vista inferior está fijada al fondo de la supervista

Ahora, supongamos que tenemos referencias a las cuatro vistas como views , una matriz. Entonces:

 let guides = [UILayoutGuide(), UILayoutGuide(), UILayoutGuide()] for guide in guides { self.view.addLayoutGuide(guide) } NSLayoutConstraint.activateConstraints([ // guide heights are equal guides[1].heightAnchor.constraintEqualToAnchor(guides[0].heightAnchor), guides[2].heightAnchor.constraintEqualToAnchor(guides[0].heightAnchor), // guide widths are arbitrary, let's say 10 guides[0].widthAnchor.constraintEqualToConstant(10), guides[1].widthAnchor.constraintEqualToConstant(10), guides[2].widthAnchor.constraintEqualToConstant(10), // guide left is arbitrary, let's say superview margin guides[0].leftAnchor.constraintEqualToAnchor(self.view.leftAnchor), guides[1].leftAnchor.constraintEqualToAnchor(self.view.leftAnchor), guides[2].leftAnchor.constraintEqualToAnchor(self.view.leftAnchor), // bottom of each view is top of following guide views[0].bottomAnchor.constraintEqualToAnchor(guides[0].topAnchor), views[1].bottomAnchor.constraintEqualToAnchor(guides[1].topAnchor), views[2].bottomAnchor.constraintEqualToAnchor(guides[2].topAnchor), // top of each view is bottom of preceding guide views[1].topAnchor.constraintEqualToAnchor(guides[0].bottomAnchor), views[2].topAnchor.constraintEqualToAnchor(guides[1].bottomAnchor), views[3].topAnchor.constraintEqualToAnchor(guides[2].bottomAnchor) ]) 

(Obviamente, podría hacer que ese código sea más lindo y más corto usando bucles, pero he desenrollado deliberadamente los bucles para mayor claridad, para que pueda ver el patrón y la técnica).