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.
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.
- 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
- Haz un
VStackcon un título y un subtítulo, alineados a.leading. - Crea una fila (
HStack) con un texto a la izquierda y otro a la derecha usandoSpacer(). - Superpón un
Textsobre unColorconZStack. - Haz un botón a ancho completo con
.frame(maxWidth: .infinity). - Prueba una rejilla simple con
GridyGridRow.