wandres.dev
SWIFTUI ESENCIAL · vistas y estado

Layout: stacks, grids y espaciado

Coloca las cosas en pantalla con VStack, HStack, ZStack, Spacer y Grid. Alignment, frames y el modelo de layout que hace SwiftUI predecible.

⏱ 15 min

Todo diseño de interfaz es, en el fondo, colocar cosas: en fila, en columna, encima. SwiftUI lo resuelve con contenedores simples que se combinan. Domina cuatro o cinco y podrás construir casi cualquier pantalla.

🎯 Al terminar esta lección sabrás
  • Apilar con VStack, HStack y ZStack.
  • Empujar y separar con Spacer y spacing.
  • Alinear con alignment.
  • Rejillas con Grid y control de tamaño con frame.

Los tres stacks

⬇️

VStack

Apila en vertical (columna). De arriba abajo.

➡️

HStack

Apila en horizontal (fila). De izquierda a derecha.

🔲

ZStack

Apila en profundidad (capas). Uno encima de otro.

VStack {
    Text("Arriba")
    Text("Abajo")
}

HStack {
    Image(systemName: "star")
    Text("Al lado")
}

ZStack {
    Color.blue                     // fondo
    Text("Encima").foregroundStyle(.white)
}

Espaciado y Spacer

Cada stack acepta spacing para separar sus hijos. Y Spacer() es un elemento elástico que empuja el resto:

VStack(spacing: 20) {          // 20 puntos entre elementos
    Text("Título")
    Text("Subtítulo")
}

HStack {
    Text("Izquierda")
    Spacer()                   // empuja hacia los extremos
    Text("Derecha")
}
💡
Spacer es tu mejor amigo para alinear

En vez de calcular márgenes, deja que Spacer() haga el trabajo: uno antes empuja a la derecha/abajo, uno después a la izquierda/arriba, uno a cada lado centra. Es la forma más natural de distribuir espacio en SwiftUI.

Alignment

Cada stack alinea sus hijos en el eje perpendicular. Un VStack alinea en horizontal:

VStack(alignment: .leading) {   // .leading, .center, .trailing
    Text("Alineado")
    Text("a la izquierda")
}

HStack(alignment: .top) {        // .top, .center, .bottom
    Text("Arriba")
    Text("del todo")
}

Grid

Para rejillas de filas y columnas alineadas, Grid (iOS 16+):

Grid {
    GridRow {
        Text("Nombre"); Text("Ana")
    }
    GridRow {
        Text("Edad"); Text("30")
    }
}

Para colecciones que se desplazan, usarás LazyVGrid / LazyHGrid (cargan solo lo visible), que verás con las listas.

frame: controlar el tamaño

Por defecto, una vista ocupa lo que necesita. frame te deja fijar tamaños o pedir que se expanda:

Text("Botón")
    .frame(maxWidth: .infinity)      // ocupa todo el ancho disponible
    .frame(height: 50)
    .background(.blue)

maxWidth: .infinity es el truco para que un elemento se estire hasta llenar su contenedor — habitual en botones a ancho completo.

El layout de SwiftUI es una negociación

El modelo mental correcto: el padre propone un tamaño al hijo, el hijo elige el suyo dentro de esa propuesta, y el padre lo coloca. Un Text elige lo justo para su contenido; con .frame(maxWidth: .infinity) elige estirarse. Cuando algo no se coloca donde esperas, piensa en esa conversación padre↔hijo: casi siempre el hijo está eligiendo un tamaño distinto al que imaginabas. Entender esta negociación te ahorra la frustración clásica del principiante con el layout.

⚔️ Compón una pantalla
  1. Haz un VStack con un título y un subtítulo, alineados a .leading.
  2. Crea una fila (HStack) con un texto a la izquierda y otro a la derecha usando Spacer().
  3. Superpón un Text sobre un Color con ZStack.
  4. Haz un botón a ancho completo con .frame(maxWidth: .infinity).
  5. Prueba una rejilla simple con Grid y GridRow.