Cómo hacer que el diseño automático sea más conveniente en iOS

¡Gracias a unDraw por sus excelentes ilustraciones!

El diseño automático ha existido desde macOS 10.7 e iOS 6.0 como una mejor manera de hacer diseños sobre las viejas máscaras de cambio de tamaño. Además de algunos casos raros en los que necesitamos especificar manualmente los orígenes y tamaños, el diseño automático es la forma preferida de hacer diseños declarativos si elegimos hacer la interfaz de usuario en código o Storyboard. Las API de diseño automático tienen algunas mejoras a lo largo de los años, también hay algunas bibliotecas de azúcar que agregan sintaxis fáciles, por lo que hay más opciones para los desarrolladores.

En este artículo, revisemos cómo ha mejorado el diseño a lo largo de los años, desde el diseño manual, las máscaras de autorización y, finalmente, hasta el diseño automático. Mencionaré algunas versiones de algunas bibliotecas y cómo abstraer el diseño automático utilizando el patrón de construcción. También hay algunas notas sobre el ciclo de vida de la vista y las mejoras de las API que podrían pasarse por alto. Por supuesto, estos se basan en mi experiencia y varían según los gustos personales, pero espero que encuentres algo útil.

Aunque el artículo menciona iOS, el mismo aprendizaje se aplica a watchOS, tvOS y macOS también. Aquí hay un adelanto de lo que aprenderemos hoy:

  • Posicionar una vista antes del diseño automático (diseño manual usando CGRect, por qué el tamaño de la vista es correcto en viewDidLoad, máscaras de autoresizing)
  • Diseño automático al rescate (un sistema de diseño basado en restricciones, traduce AutoresizingMaskIntoConstraints, Visual Format Language, addConstraint y activar, NSLayoutAnchor y mensaje de error)
  • Abstracciones sobre el diseño automático (Cartografía, SnapKit, Las muchas funciones de sobrecarga)
  • Abrazando el diseño automático
  • Hacer que el diseño automático sea más conveniente con el patrón de construcción (¿Qué objetos pueden interactuar con el Diseño automático? ¿Qué propiedades se necesitan en una restricción de diseño? Inferir y recuperar restricciones)
  • A dónde ir desde aquí (también conocido como Resumen).

Posicionar una vista antes del diseño automático

Cuando comencé la programación de iOS a principios de 2014, leí un libro sobre Auto Layout y ese libro detallaba muchos escenarios que me confundían por completo. No pasó mucho tiempo hasta que probé el diseño automático en una aplicación y me di cuenta de que era tan simple. En su sentido más simple, una vista necesita una posición y un tamaño para mostrarse correctamente en la pantalla, todo lo demás es simplemente extra. En el término de diseño automático, necesitamos especificar suficientes restricciones para posicionar y dimensionar la vista.

Diseño manual usando CGRect

Si echamos un vistazo a la forma en que hacemos un diseño manual con el marco, hay orígenes y tamaños. Por ejemplo, aquí se explica cómo colocar un cuadro rojo que se estira de acuerdo con el ancho del controlador de vista.

Se llama a viewDidLoad cuando se carga la propiedad de vista del controlador de vista, debemos esperar hasta que viewDidLayoutSubviews tenga acceso a los límites finales. Cuando los límites cambian para la vista de un controlador de vista, la vista ajusta las posiciones de sus subvistas y luego el sistema llama a este método.

¿Por qué el tamaño de vista es correcto en viewDidLoad?

viewDidLoad definitivamente no es la forma recomendada de hacer un diseño manual, pero a veces vemos que su vista tiene el tamaño correcto y llena la pantalla. Esto es cuando necesitamos leer más detalladamente la Gestión de vistas en UIViewController:

Cada controlador de vista gestiona una jerarquía de vistas, cuya vista raíz se almacena en la propiedad de vista de esta clase. La vista raíz actúa principalmente como un contenedor para el resto de la jerarquía de vistas. El tamaño y la posición de la vista raíz está determinada por el objeto que la posee, que es un controlador de vista principal o la ventana de la aplicación. El controlador de vista que es propiedad de la ventana es el controlador de vista raíz de la aplicación y su vista está dimensionada para llenar la ventana.
La vista raíz de un controlador de vista siempre tiene el tamaño adecuado para su espacio asignado.

También puede ser que la vista tenga un tamaño fijo en xib o storyboard, pero debemos controlar el tamaño explícitamente y hacerlo en el método del controlador de vista correcto para evitar comportamientos inesperados.

Mascarillas Autoresizantes

La máscara de autoresizing era la antigua forma de hacer que el diseño fuera un poco más declarativo, también llamado sistema de diseño de resortes y puntales. Es una máscara de bits entera que determina cómo el receptor cambia de tamaño cuando cambian los límites de su supervista. La combinación de estas constantes le permite especificar qué dimensiones de la vista deberían aumentar o reducirse en relación con la supervista. El valor predeterminado de esta propiedad es none, lo que indica que la vista no debe cambiar su tamaño.

Si bien podemos especificar qué bordes queremos arreglar y cuáles deberían ser flexibles, es confuso en la forma en que lo hacemos en xib y en el código.

En la captura de pantalla anterior, fijamos la parte superior del cuadro rojo en la parte superior de la pantalla, y eso es una distancia fija. Cuando la vista cambia de tamaño, el ancho y la altura del cuadro rojo cambian proporcionalmente, pero el espacio superior permanece fijo.

En el código, en lugar de especificar autoresizing en términos de distancia fija, usamos términos flexibles para especificar qué bordes deben ser flexibles.

Para lograr el mismo cuadro rojo en la parte superior de la pantalla, necesitamos especificar un ancho flexible y un margen inferior flexible. Esto significa que los bordes izquierdo, derecho y superior están fijos.

box.autoresizingMask = [.flexibleWidth, .flexibleBottomMargin]

Aquí hay algunos escenarios más

  • Distancia horizontalmente fija desde la izquierda: [.flexibleRightMargin]
  • Centrar horizontalmente [.flexibleLeftMargin, .flexibleRightMargin]
  • Distancia verticalmente fija desde la parte superior: [.flexibleBottomMargin]
  • Centrar verticalmente [.flexibleTopMargin, .flexibleBottomMargin]

Estos no son muy intuitivos y la forma en que se escalan proporcionalmente puede no ajustarse a nuestras expectativas. Además, tenga en cuenta que se pueden hacer múltiples opciones en el mismo eje.

Cuando se establece más de una opción a lo largo del mismo eje, el comportamiento predeterminado es distribuir la diferencia de tamaño proporcionalmente entre las porciones flexibles. Cuanto más grande sea la porción flexible, en relación con las otras porciones flexibles, es más probable que crezca. Por ejemplo, suponga que esta propiedad incluye las constantes flexibleWidth y flexibleRightMargin pero no incluye la constante flexibleLeftMargin, lo que indica que el ancho del margen izquierdo de la vista es fijo pero que el ancho y el margen derecho de la vista pueden cambiar.

La comprensión de las máscaras de tamaño automático no perderá su tiempo, volveremos a ello en unos minutos

Disposición automática al rescate

El diseño automático, junto con el texto dinámico y las clases de tamaño, son formas recomendadas de crear interfaces de usuario adaptativas a medida que aumenta la cantidad de dispositivos iOS con diferentes tamaños de pantalla.

Un sistema de diseño basado en restricciones

El diseño automático se describe a través de NSLayoutConstraint definiendo restricciones entre 2 objetos. Aquí está la fórmula simple para recordar:

item1.attribute1 = multiplicador × item2.attribute2 + constante

Aquí se explica cómo replicar ese cuadro rojo con NSLayoutConstraint. Necesitamos especificar qué propiedad de qué vista se debe conectar a otra propiedad de otra vista. El diseño automático admite muchos atributos, como ascenterX, centerY y topMargin.

traduceAutoresizingMaskIntoConstraints

Si ejecutamos el código anterior, ingresaremos al popular mensaje de advertencia sobre las traducciones de AutoresizingMaskIntoConstraints

[LayoutConstraints] No se pueden satisfacer simultáneamente las restricciones.
 Probablemente al menos una de las restricciones en la siguiente lista es una que no desea.
 Prueba esto:
  (1) mire cada restricción e intente descubrir cuál no espera;
  (2) encuentre el código que agregó la restricción o restricciones no deseadas y arréglelo.
 (Nota: si está viendo NSAutoresizingMaskLayoutConstraints que no entiende, consulte la documentación de la propiedad UIView traduceAutoresizingMaskIntoConstraints)
(
    "",
    ""
)

Si necesita ayuda para descifrar esto, hay wtfautolayout que hace un buen trabajo al explicar lo que realmente está sucediendo.

Se dice que el cambio de tamaño de las máscaras se ha vuelto a implementar utilizando el diseño automático debajo del capó, y siempre se agrega NSAutoresizingMaskLayoutConstraint a la vista, de ahí la restricción midX.

Nunca debemos mezclar máscaras de cambio de tamaño y diseño automático para evitar comportamientos no deseados, la solución es simplemente deshabilitar las traducciones de AutoresizingMaskIntoConstraints

box.translatesAutoresizingMaskIntoConstraints = false

Esta propiedad es falsa de forma predeterminada para las vistas desde xib o storyboard, pero se cumple si declaramos el diseño en código. La intención es que el sistema cree un conjunto de restricciones que dupliquen el comportamiento especificado por la máscara de autorización de la vista. Esto también le permite modificar el tamaño y la ubicación de la vista utilizando el marco, los límites o las propiedades del centro de la vista, lo que le permite crear un diseño estático basado en marcos dentro del diseño automático.

Lenguaje de formato visual

El lenguaje de formato visual le permite usar cadenas similares al arte ASCII para definir sus restricciones. Veo que se usa en algunas bases de código, así que es bueno saberlo.

Aquí es cómo recrear el cuadro rojo usando VFL. Necesitamos especificar restricciones para la dirección horizontal y vertical. Tenga en cuenta que la misma cadena de formato puede dar lugar a múltiples restricciones:

El lenguaje de formato visual es un poco mejor que el inicializador detallado NSLayoutConstraint, pero alienta el formato de cadena, que es propenso a errores.

addConstraint y active

Esto puede parecer trivial, pero veo en las bases de código modernas, todavía hay uso de addConstraint. Esto era antiguo y difícil de usar, ya que debemos encontrar la vista de antepasado común más cercana de las 2 vistas que se incluyen en el diseño automático.

A partir de iOS 8, hay isActive y las funciones de activación estática que facilitan mucho este proceso de agregar restricciones. Básicamente, lo que hace es activar o desactivar la restricción con llamadas a toaddConstraint (_ :) y removeConstraint (_ :) en la vista que es el ancestro común más cercano de los elementos administrados por esta restricción.

NSLayoutAnchor

Comenzando con macOS 10.11 e iOS 9, había NSLayoutAnchor que simplifica mucho el diseño automático. El diseño automático fue declarativo, pero un poco detallado, ahora es más simple que nunca con un sistema de anclaje.

Aquí es cómo lograr ese mismo cuadro rojo

NSLayoutConstraint.activate ([
    box.topAnchor.constraint (equalTo: view.topAnchor, constante: 50),
    box.leftAnchor.constraint (equalTo: view.leftAnchor, constante: 20),
    box.rightAnchor.constraint (equalTo: view.rightAnchor, constante: -20),
    box.heightAnchor.constraint (equalToConstant: 100)
])

Lo bueno de NSLayoutAnchor es su diseño genérico de API restringida. Las restricciones se dividen en el eje X, el eje Y y el tipo de ancla de dimensión que hace que sea difícil cometer errores.

clase abierta NSLayoutXAxisAnchor: NSLayoutAnchor 
clase abierta NSLayoutDimension: NSLayoutAnchor 

Por ejemplo, no podemos fijar el ancla superior al ancla izquierda ya que están en diferentes ejes, y no tiene sentido. Intente hacer los siguientes resultados en el problema de compilación ya que el sistema de tipo fuerte Swift garantiza la corrección.

box.topAnchor.constraint (equalTo: view.leftAnchor, constante: 50),

Mensaje de error ambiguo con NSLayoutAnchor

He entrado en algunos casos donde los mensajes de error de NSLayoutAnchor no ayudan. Si por error conectamos topAnchor con centerXAnchor, que no son posibles ya que son de diferentes ejes.

NSLayoutConstraint.activate ([
    box.topAnchor.constraint (equalTo: view.centerXAnchor, constante: 50)
])

Xcode se queja de un problema UIView sin envolver que puede confundirnos aún más.

Valor del tipo opcional 'UIView?' debe desenvolverse para referirse al miembro 'centerXAnchor' del tipo de base envuelta 'UIView'

Otro acertijo sobre este código.

NSLayoutConstraint.activate ([
    imageView.centerYAnchor.constraint (equalTo: view.centerYAnchor),
    imageView.leftAnchor.constraint (equalTo: view.leftAnchor, constante: 8),
    imageView.heightAnchor.constraint (equalToConstant: view.heightAnchor, mult0.7),
    imageView.widthAnchor.constraint (equalTo: imageView.heightAnchor, multiplicador: 1.0)
])

Xcode se queja de que "Int" no es convertible a "CGFloat", lo cual es muy engañoso. ¿Puedes detectar el error?

El problema es que estamos usando equalToConstant, no equalTo. Las restricciones genéricas de NSLayoutAnchor nos están dando errores engañosos y pueden perder mucho tiempo tratando de descubrir los errores tipográficos sutiles.

Abstracciones sobre el diseño automático

NSLayoutAnchor se está volviendo más popular ahora, pero no está exento de defectos. Dependiendo del gusto personal, puede haber otras formas de abstracción sobre Auto Layout, a saber, Cartografía y SnapKit, que he usado y amado. Estas son algunas de mis opiniones sobre esos.

Cartografía

La cartografía es una de las formas más populares de hacer diseño automático en iOS. Utiliza operadores que hacen que las restricciones sean muy claras:

restringir (botón1, botón2) {botón1, botón2 en
    button1.right == button2.left - 12
}

Lo que no me gusta de la Cartografía es que tenemos que repetir los nombres de los parámetros y que los parámetros dentro del cierre son solo proxies, no vistas reales y hay un límite en el número de elementos de restricción.

Otra gran desventaja fue el largo tiempo de compilación debido al uso excesivo de operadores. Tiempo de compilación muy largo en funciones grandes. Aunque el tiempo de compilación de Swift está mejorando, este fue un gran problema. Incluso tuve que escribir un script para eliminar Cartography para usar NSLayoutAnchor simple, así que eche un vistazo a AutoLayoutConverter, que convierte el código de Cartography de

restringir (logoImnageView, view1, view2) {logoImageView, view1, view2 en
    logoImageView.with == 74
    view1.left == view2.left + 20
}

a NSLayoutAnchor simple

Restricción.on (
  logoImageView.widthAnchor.constraint (equalToConstant: 74),
  view1.leftAnchor.constraint (equalTo: view2.leftAnchor, constante: 20),
)

Siempre hay compensaciones, pero reducir el tiempo de compilación en ese momento era una prioridad.

SnapKit

SnapKit, originalmente Masonry, es quizás el envoltorio de diseño automático más popular

box.snp.makeConstraints {(make) -> Anulado en
   make.width.height.equalTo (50)
   make.center.equalTo (self.view)
}

La sintaxis es agradable y con un espacio de nombres snp para evitar conflictos de nombres de extensiones, lo cual me encanta.

Lo que no me gusta de SnapKit es ese cierre limitado. Solo podemos trabajar en 1 vista a la vez, y el cierre interno es solo un proxy, lo que no parece intuitivo.

Imagínese si vamos a hacer vistas de paginación o piano, donde cada vista se apila una al lado de la otra. Necesitamos muchas llamadas SnapKit ya que solo podemos trabajar en 1 vista a la vez. Además, no hay una relación clara donde nos conectamos con la otra vista.

https://github.com/onmyway133/blog/issues/22
keyB.snp.makeConstraints {(make) -> Anulado en
   make.left.equalTo (self.keyA.right)
}
keyC.snp.makeConstraints {(make) -> Anulado en
   make.left.equalTo (self.keyB.right)
}
keyD.snp.makeConstraints {(make) -> Anulado en
   make.left.equalTo (self.keyC.right)
}

Las muchas funciones de sobrecarga

También hay intentos de construir funciones simples de envoltura de diseño automático, pero eso se intensifica muy rápidamente.

Podríamos comenzar con una extensión que fija las restricciones de borde a la supervista.

box.pinEdgesToSuperview ()

Pero una vista no siempre se fija a su supervista, puede ser a otra vista, luego agregamos otra función

box.pinEdgesToView (_ view: UIView)

Sería bueno si hay algo de relleno, ¿no? Agreguemos opciones de inserción

box.pinEdgesToView (_ view: UIView, insets: UIEdgeInsets)

Puede haber casos en los que solo queremos fijar la parte superior, izquierda y derecha y no la parte inferior, agreguemos otro parámetro

box.pinEdgesToView (_ view: UIView, insets: UIEdgeInsets, exclude: NSLayoutConstraint.Attribute)

Las restricciones no siempre son 1000 prioridades, puede ser menor. Necesitamos apoyar eso

box.pinEdgesToView (_ view: UIView, insets: UIEdgeInsets, excluir: NSLayoutConstraint.Attribute, prioridad: NSLayoutConstraint.Priority)

Podríamos excluir más de una propiedad o establecer diferentes niveles de prioridad para cada restricción. El contenedor simple con funciones de sobrecarga y parámetros predeterminados es como construir abstracciones rígidas basadas en suposiciones prematuras. Esto solo nos limita a largo plazo y no es escalable

Abrazando el diseño automático

Todos los marcos de diseño automático que existen son solo formas convenientes de construir NSLayoutConstraint, de hecho, estos son lo que normalmente necesita

  • Llame a addSubview para que esa vista esté en la jerarquía
  • Establecer traduccionesAutoresizingMaskIntoConstraints = false
  • Establezca isActive = true para habilitar restricciones

Aquí le mostramos cómo hacer una extensión en NSLayoutConstraint que deshabilite traductsAutoresizingMaskIntoConstraints para las vistas involucradas. El código es de Omnia

Aquí, antes de activar las restricciones, encontramos que firstItem deshabilita las traducciones de AutoresizingMaskIntoConstraints. Desde Swift 4.2 existe una separación entre compactMap y flatMap, por lo que podemos usar flatMap de manera segura para aplanar una matriz de matrices. Esto es útil cuando tenemos una matriz de matrices de restricciones.

Con eso, podemos fijar el cuadro rojo con un cierto tamaño y en el centro de la pantalla:

NSLayoutConstraint.on ([
    box.pinCenter (ver: ver),
    box.pin (tamaño: CGSize (ancho: 100, alto: 50))
])

Este es un contenedor muy delgado pero potente sobre NSLayoutAnchor y podemos expandirlo de la manera que lo necesitemos. Lamentablemente tiene algunos problemas, como que no podemos cambiar fácilmente la prioridad, ya que tenemos que hacer referencia a la restricción

deje topConstraint = box.topAnchor.constraint (equalTo: view.topAnchor, constante: 50)
topConstraint.priority = UILayoutPriority.defaultLow
NSLayoutConstraint.on ([
    topConstraint
])

Hacer que el diseño automático sea más conveniente con el patrón de construcción

La extensión anterior en NSLayoutConstraint funciona bien. Sin embargo, si usted es como yo y quiere un código de diseño automático aún más declarativo y rápido, podemos usar el patrón de creación para hacer que el diseño automático sea aún más agradable. El patrón de construcción se puede aplicar a muchas partes del código, pero me parece muy adecuado para el diseño automático. El código final es Anchors en GitHub, y detallaré cómo hacerlo.

Esto es lo que queremos lograr para posicionar rápidamente 4 vistas:

activar(
  boxA.anchor.top.left,
  boxB.anchor.top.right,
  boxC.anchor.bottom.left,
  boxD.anchor.bottom.right
)

La mayoría de las veces, queremos anclar a la vista principal, por lo que debería hacerse implícitamente por nosotros. Me gusta tener un espacio de nombres de anclaje para evitar conflictos de nombres de extensiones y convertirlo en el punto de partida de todo nuestro conveniente código de diseño automático. Identifiquemos algunos conceptos básicos

¿Qué objetos pueden interactuar con el diseño automático?

Actualmente, hay 3 tipos de objetos que pueden interactuar con el diseño automático

  • UIView
  • UILayoutSupport, desde iOS 7, para UIViewController para obtener bottomLayoutGuide y topLayoutGuide. En iOS 11, deberíamos usar safeAreaLayoutGuide de UIView en su lugar
  • UILayoutGuide: el uso de UIView invisible para el diseño automático es costoso, por eso Apple introdujo guías de diseño en iOS 9 para ayudar.

Entonces, para admitir estos 3 con el espacio de nombres de anclaje, podemos hacer que el objeto de anclaje contenga AnyObject como detrás de la escena, NSLayoutConstraint funciona con AnyObject:

Ancla de clase pública: ConstraintProducer {
  dejar elemento: AnyObject
/// Init con vista
  conveniencia init (ver: Ver) {
    self.init (elemento: ver)
  }
/// Inicia con guía de diseño
  conveniencia init (layoutGuide: LayoutGuide) {
    self.init (item: layoutGuide)
  }
// Init con artículo
  public init (elemento: AnyObject) {
    self.item = item
  }
}

Ahora podemos definir la propiedad del ancla

Vista de extensión pública {
  var anchor: Anchor {
    Ancla de retorno (vista: auto)
  }
}
extensión pública LayoutGuide {
  var anchor: Anchor {
    return Anchor (layoutGuide: self)
  }
}

¿Qué propiedades se necesitan en una restricción de diseño?

Los patrones de construcción hacen que la construcción de cosas sea declarativa al mantener valores temporales. Además del identificador de prioridad a, necesitamos una matriz de pines para manejar casos en los que existen múltiples restricciones creadas. Una restricción central da como resultado restricciones centerX y centerY, y una restricción de borde da como resultado restricciones superior, izquierda, inferior y derecha.

Con esto, también podemos expandir nuestro anclaje conveniente para admitir más restricciones, como el espaciado horizontal, que agrega restricciones izquierda y derecha con constantes correctas. Porque, como saben, en Diseño automático, para la dirección derecha e inferior, necesitamos usar valores negativos:

Func paddingHorizontally (_ value: CGFloat) -> Anchor {
  removeIfAny (.leading)
  removeIfAny (.trailing
  pins.append (Pin (.leading, constante: valor))
  pins.append (Pin (.trailing, constante: -value)
  volver a sí mismo
}

Restricciones inferidas

Hay veces que queremos inferir restricciones, como si queremos que la altura de una vista duplique su ancho. Como ya tenemos ancho, la relación de declaración debe emparejar la altura con el ancho.

box.anchor.width.constant (10)
box.anchor.height.ratio (2) // height == width * 2

Esto se logra fácilmente al revisar nuestra matriz de pines

if sourceAnchor.exists (.width) {
  return Anchor (item: sourceAnchor.item) .width
    .igual
    .to (Anchor (item: sourceAnchor.item) .height)
    .multiplier (ratio) .constraints ()
} else if sourceAnchor.exists (.height) {
  return Anchor (item: sourceAnchor.item) .height
    .igual
    .to (Anchor (elemento: sourceAnchor.item) .width)
    .multiplier (ratio) .constraints ()
} más {
  regreso []
}

Recuperando una restricción

Veo que estamos acostumbrados a almacenar la propiedad de restricción para cambiar su constante más adelante. La propiedad de restricciones en UIView tiene suficiente información y es la fuente de la verdad, por lo que es más preferible recuperar las restricciones.

Así es como encontramos restricciones y actualizaciones que

boxA.anchor.find (.height)? constante = 100
// más tarde
boxB.anchor.find (.height)? constante = 100
// más tarde
boxC.anchor.find (.height)? constante = 100

El código para encontrar la restricción es muy sencillo.

Cómo restablecer restricciones

Uno de los patrones que veo en todas partes es restablecer restricciones en UITableViewCell o UICollectionViewCell. Dependiendo del estado, la celda elimina ciertas restricciones y agrega nuevas restricciones. La cartografía hace esto bien usando el grupo.

restringir (ver, reemplazar: grupo) {ver en
    view.top == view.superview! .top
    view.left == view.superview! .left
}

Si lo pensamos bien, NSLayoutConstraint es solo instrucciones de diseño. Se puede activar o desactivar. Entonces, si podemos agrupar restricciones, podemos activarlas o desactivarlas como un todo.

Aquí se explica cómo declarar 4 grupos de restricciones, la sintaxis es de Anchors, pero esto también se aplica a NSLayoutAnchor, ya que generan NSLayoutConstraint debajo del capó.

let g1 = group (box.anchor.top.left)
let g2 = group (box.anchor.top.right)
let g3 = group (box.anchor.bottom.right)
let g4 = group (box.anchor.bottom.left)

A dónde ir desde aquí

En este artículo, avanzamos un poco en el diseño manual, enmascarando las máscaras y luego en el diseño automático moderno. Las API de diseño automático tienen mejoras a lo largo de los años y son una forma recomendada de hacer el diseño. Aprender el diseño declarativo también me ayuda mucho cuando aprendo el diseño de restricciones en Android, flexbox en React Native o el diseño del widget en Flutter.

La publicación revisa la implementación detallada de cómo podemos construir un diseño automático más conveniente como los anclajes con el patrón de construcción. En el siguiente artículo, exploremos las muchas formas de depurar el diseño automático y cómo hacer correctamente el diseño automático para diferentes tamaños de pantalla.

Mientras tanto, juguemos Tetris en Auto Layout, porque ¿por qué no

https://github.com/onmyway133/Anchors
activar(
  lineBlock.anchor.left.bottom
)
// más tarde
activar(
  firstSquareBlock.anchor.left.equal.to (lineBlock.anchor.right),
  firstSquareBlock.anchor.bottom
)
// más tarde
activar(
  secondSquareBlock.anchor.right.bottom
)