Autolayout por código en Swift

Para crear una aplicación con UIKit en Swift debes conocer cómo colocar las vistas en la pantalla y para eso necesitas usar las constraints. En este caso explicamos constraints por código.

SwiftBeta

Tabla de contenido


👇 SÍGUEME PARA APRENDER SWIFTUI, SWIFT, XCODE, etc 👇

Al crear proyectos con UIKit, verás que es inevitable usar constraints. Las constraints lo que hacen es colocar y adaptar nuestros componentes de UI según distintos tamaños de pantalla. Por lo tanto si quieres meter cualquier vista, ésta debe tener unas reglas que marcaran su aspecto.

Hay distintas maneras de crear constraints:

  • Por código
  • Usando interface builder

¿Cúal utilizar? si trabajas solo con el que te sientas más cómodo, y si trabajas en equipos grandes mi consejo es que lleguéis a un acuerdo. Al principio lo hacía con interface builder, pero después entré en un equipo bastante grande. Allí lo hacían todo por código, al principio lo ví un poco raro, pero me acostumbré y la verdad que no echo de menos interface builder.

En este post nos vamos a centrar en usar constraints por código. Para ello crearemos una aplicación sencilla (podréis encontrar el código que hemos usado en este post aquí).

Lo primero que haremos es añadir código a nuestro ViewController:

import UIKit

class ViewController: UIViewController {
    // 1
    let nameLabel: UILabel = {
        let label = UILabel()
        label.numberOfLines = 0
        label.text = "SwiftBeta"
        label.translatesAutoresizingMaskIntoConstraints = false
        return label
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // 2
        view.addSubview(nameLabel)
        
        // 3
        NSLayoutConstraint.activate([
            nameLabel.centerYAnchor.constraint(equalTo: view.centerYAnchor),
            nameLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor),
        ])
        
    }
}

Lo que hemos hecho es:

  1. Crear una vista de tipo UILabel. Es muy impotante que asignemos a la propiedad translatesAutoresizingMaskIntoConstraints el valor de true. Si no lo asignamos a true las constraints no tendrán efecto.
  2. Añadirla a la vista padre. En este caso es toda la pantalla que ocupa el iPhone.
  3. Aplicar constraints para que estén en el centro de la pantalla. Hemos cogido el centro del label y hemos especificado que tiene que tener el mismo centro que su vista padre (Cuando decimos vista padre, es la vista contenedora, la que ha añadido el label).

Como resultado tenemos un label en el centro de la pantalla que pone "SwiftBeta Infinito".

También podríamos haber aplicado las constraints de esta manera:

nameLabel.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
nameLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
Aunque prefiero el primer ejemplo, ya que es más legible y así no tenemos que repetir el isActive todo el rato

Si escribes nameLabel.anchor verás que aparece una lista con todas estas propiedades:

leadingAnchor trailingAnchor
leftAnchor rightAnchor
topAnchor bottomAnchor
widthAndhor heightAnchor
centerXAnchor centerYAnchor

Cada opción es para aplicar un tipo de constraint, vamos a ver cada una

LeadingAnchor

Con .leadingAnchor podemos crear una constraint que se refiere a la parte izquierda de nuestra vista. En el ejemplo hemos creado una constraint que coge el lado izquierdo de nuestra vista y la pone en el lado izquierda de la vista padre.

TrailingAnchor

Con .trailingAnchor podemos crear una constraint que se refiere a la parte derecha de nuestra vista. En el ejemplo hemos creado una constraint que coge el lado derecho de nuestra vista y la pone en el lado derecho de la vista padre.

⚠️ La diferencia entre leadingAnchor y leftAnchor o trailingAnchor y rightAnchor es que cuando usamos leftAnchor y rightAnchor siempre nos basamos en el lado izquierdo o derecho del dispositivo, mientras que si usamos leadingAnchor y trailingAnchor depende de la configuración regional del dispositivo. Hay idiomas que se leen de izquierda a derecha, como el español, inglés, etc pero hay otros idiomas que se leen de derecha a izquierda.

TopAnchor

Con .topAnchor podemos crear una constraint que se refiere a la parte superior de nuestra vista. En el ejemplo hemos creado una constraint que coge la parte superior de nuestra vista y la pone en la parte de arriba de la vista padre.

BottomAnchor

Con .bottomAnchor podemos crear una constraint que se refiere a la parte inferior de nuestra vista. En el ejemplo hemos creado una constraint que coge la parte inferior de nuestra vista y la pone en la parte de abajo de la vista padre.

WidthAnchor

Con .widthAnchor podemos crear una constraint que se refiere al width de nuestra vista. En el ejemplo hemos creado una constraint que coge el mismo width que la vista padre.

HeightAnchor

Con .heightAnchor podemos crear una constraint que se refiere al height de nuestra vista. En el ejemplo hemos creado una constraint que coge el mismo height que la vista padre.

CenterXAnchor

Con .centerXAnchor podemos crear una constraint que se refiere al centro en el eje X de nuestra vista. En el ejemplo hemos creado una constraint que coge el mismo centro en el eje X que la vista padre.

CenterYAnchor

Con .centerYAnchor podemos crear una constraint que se refiere al centro en el eje Y de nuestra vista. En el ejemplo hemos creado una constraint que coge el mismo centro en el eje Y que la vista padre.


Acabamos de ver todos los tipos de anchor que tenemos en una vista. Necesitamos un mínimo de constraints para que la vista sepa "colocarse". Es decir, la mayoría de veces necesitamos un width, un height, una constraint en el eje horizontal y otra en el eje vertical.

En el caso de UILabel, como has visto en el ejemplo del principio, solo necesitamos una constraint en el eje X y otra en el eje Y. El width y height lo saca automáticamente. Pero hay casos como UIImageView que necesitamos también el width y height.

Vamos añadir un UIImageView al ejemplo anterior, para ello creamos una nueva propiedad:

let imageView: UIImageView = {
    let imageView = UIImageView()
    imageView.backgroundColor = .blue
    imageView.translatesAutoresizingMaskIntoConstraints = false
    imageView.layer.cornerRadius = 100
    return imageView
}()

Lo que vamos hacer es añadir esta UIImageView encima de nuestro label, para ello añadimos las nuevas constraint en nuestro NSLayoutConstraint.activateNested y quedarían de la siguient manera:

NSLayoutConstraint.activate([
    nameLabel.centerYAnchor.constraint(equalTo: view.centerYAnchor),
    nameLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor),
    
    imageView.widthAnchor.constraint(equalToConstant: 200),
    imageView.heightAnchor.constraint(equalToConstant: 200),
    imageView.centerXAnchor.constraint(equalTo: nameLabel.centerXAnchor),
    imageView.bottomAnchor.constraint(equalTo: nameLabel.topAnchor, constant: -16)
])

Fíjate, hemos asignado un valor constante de 200 al tamaño de nuestro UIImageView, lo hemos centrado en el eje de las X igual que nuestro label y por último hemos dicho que la parte de abajo del UIImageView está unido a la parte superior del UILabel. Para añadir un pequeño espacio, lo separamos 16 puntos.

Y así podemos ir creando nuestras vistas con código. Hemos visto varios métodos para añadir constraints, pero disponemos de más:

podemos usar equalTo, lessThanOrEqualTo, greaterThanOrEqualTo.
Al usar equalTo cogemos el valor exacto y al usar lessThanOrEqualTo o greaterThanOrEqualTo hacemos que el valor de la constraint sea más flexible.

También podemos usar multipliers, por si queremos coger el 0.5 del width de una vista. Tenemos mucha flexibilidad y podemos hacer de todo creando constraints por código.


Guías en layout

A parte de ls constraints que hemos visto, tenemos algunas con un objetivo más específico:

  • layoutMarginsGuide: Guía que se usa con un pequeño margen en el top, bottom, leading y trailing de 8 puntos.
  • readableContentGuide: Guía que ajusta el width a un tamaño que facilite la lectura del usuario.
  • safeAreaLayoutGuide: Guía que representa la parte de la vista que no está tapada por barras u otros elementos.
⚠️ Esta guías varían dependiendo de si es un dispositivo con notch o sin notch (por ejemplo: iPhone X y iPhone 8)

Vamos a ver dos ejemplos, el cambio es muy pequeño, pero puedes apreciar las diferencias.

Hasta aquí el post de hoy, gracias por leernos! 🤓
Si tienes preguntas no dudes en contactar con nosotros a través de Twitter