En el mundo del desarrollo de interfaces modernas, la adaptabilidad no es un lujo, es una necesidad absoluta. Un iPhone SE, un iPad Pro de 12.9 pulgadas, un Apple Watch Ultra y una ventana redimensionable en macOS tienen algo en común: todos ejecutan tu código SwiftUI, pero ninguno ofrece el mismo lienzo.
Aquí es donde muchos desarrolladores novatos (y no tan novatos) se topan con una pared. SwiftUI es fantástico para el diseño declarativo y apilable (VStack, HStack), pero ¿qué sucede cuando necesitas precisión quirúrgica? ¿Qué pasa si necesitas que una vista mida exactamente un tercio de la pantalla, o quieres crear una animación de paralaje basada en la posición de scroll?
La respuesta es una herramienta poderosa, a menudo malinterpretada y a veces temida: GeometryReader.
En este tutorial exhaustivo, desglosaremos qué es, cómo funciona su matemática interna, y cómo implementarlo correctamente en iOS, macOS y watchOS.
1. ¿Qué es GeometryReader? Anatomía de un Contenedor
Para entender GeometryReader, primero debemos entender cómo SwiftUI renderiza las vistas. Normalmente, en SwiftUI, los padres proponen un tamaño a sus hijos, y los hijos eligen su propio tamaño.
GeometryReader rompe ligeramente este molde. Es un contenedor de vista que tiene dos características fundamentales:
- Es expansivo (Greedy): Intenta llenar todo el espacio que su padre le ofrece. Si pones un
GeometryReaderdentro de una pantalla vacía, ocupará toda la pantalla. - Expone su propia geometría: A través de un objeto llamado
GeometryProxy, le dice a sus vistas hijas: “Oigan, este es el tamaño exacto que tengo y esta es mi posición en la pantalla”.
El GeometryProxy
Cuando instancias un GeometryReader, recibes un cierre (closure) con un parámetro, usualmente llamado proxy o geometry.
GeometryReader { proxy in
// Tu código aquí
}Este proxy es la llave maestra. Contiene:
size: UnCGSizecon el ancho (width) y alto (height) del contenedor.safeAreaInsets: Los márgenes seguros (el “notch”, la barra de inicio, etc.).frame(in: CoordinateSpace): El método más potente, que permite saber dónde está la vista en relación con diferentes sistemas de coordenadas.
2. Implementación Básica: Dimensiones Relativas
El uso más común de GeometryReader es dimensionar elementos como un porcentaje del espacio disponible. Imagina que quieres dos rectángulos: uno que ocupe el 60% del ancho y otro el 40%.
struct BarraDeProgreso: View {
var body: some View {
GeometryReader { geometry in
HStack(spacing: 0) {
Rectangle()
.fill(Color.blue)
.frame(width: geometry.size.width * 0.6)
Rectangle()
.fill(Color.gray)
.frame(width: geometry.size.width * 0.4)
}
}
.frame(height: 50) // Limitamos la altura porque GeometryReader intenta expandirse
}
}Nota crítica: Observa el .frame(height: 50) al final. Si no lo pones, el GeometryReader intentará ocupar toda la altura vertical de la pantalla, empujando otros elementos fuera o creando espacios vacíos.
3. Profundizando: Sistemas de Coordenadas (Coordinate Spaces)
Aquí es donde la magia (y la confusión) ocurre. GeometryReader no solo te dice cuánto mide algo, sino dónde está. Pero, ¿dónde está con respecto a qué?
SwiftUI maneja tres espacios de coordenadas principales:
- .local: Las coordenadas relativas a la propia vista (el origen 0,0 es la esquina superior izquierda de la vista).
- .global: Las coordenadas relativas a toda la pantalla del dispositivo.
- .named(“Nombre”): Coordenadas relativas a una vista ancestra específica que tú has etiquetado.
Ejemplo Práctico: Efecto Paralaje (Parallax)
Vamos a crear una lista en iOS donde las imágenes cambian de tamaño o posición suavemente mientras el usuario hace scroll. Este efecto se logra midiendo la posición Y de cada celda en el espacio global.
struct ParallaxView: View {
var body: some View {
ScrollView {
VStack(spacing: 20) {
ForEach(0..<10) { _ in
GeometryReader { proxy in
let minY = proxy.frame(in: .global).minY
Image("paisaje")
.resizable()
.aspectRatio(contentMode: .fill)
// Magia matemática para el paralaje
.offset(y: -minY * 0.5)
.frame(width: proxy.size.width, height: proxy.size.height)
.clipped()
}
.frame(height: 200)
}
}
}
}
}En este código, proxy.frame(in: .global).minY nos dice qué tan lejos está la parte superior de la imagen respecto al borde superior de la pantalla. Usamos ese valor para desplazar la imagen interna en sentido contrario, creando la ilusión de profundidad.
4. Estrategias Multiplataforma: iOS, macOS y watchOS
Aunque el código es el mismo, la aplicación de GeometryReader varía según el dispositivo.
macOS: Ventanas Resizables
En macOS, el usuario puede cambiar el tamaño de la ventana en cualquier momento. GeometryReader es vital aquí para reorganizar layouts complejos.
Imagina un panel lateral que debe colapsar si el ancho de la ventana es menor a 500px.
// Ejemplo conceptual para macOS
GeometryReader { proxy in
HStack {
if proxy.size.width > 500 {
SidebarView()
.frame(width: 200)
}
MainContentView()
}
}Consejo: En macOS, el redimensionamiento dispara muchas actualizaciones de la vista. Mantén la lógica dentro del GeometryReader ligera para evitar caídas de FPS.
watchOS: El Espacio es Oro
En el Apple Watch, las pantallas son diminutas. Usar GeometryReader para calcular tamaños de texto dinámicos o gráficos circulares (como los anillos de actividad) es muy común.
// Ajustando un anillo de actividad al ancho del reloj
GeometryReader { geometry in
ZStack {
Circle()
.stroke(lineWidth: 10)
.foregroundColor(.gray.opacity(0.3))
Circle()
.trim(from: 0.0, to: 0.7)
.stroke(style: StrokeStyle(lineWidth: 10, lineCap: .round))
.foregroundColor(.green)
.rotationEffect(.degrees(-90))
}
// Usamos el menor de los dos valores para asegurar un círculo perfecto
.frame(width: min(geometry.size.width, geometry.size.height))
}5. El “Lado Oscuro”: Errores Comunes y Cómo Evitarlos
El error más grande al usar GeometryReader es olvidar que rompe el layout natural de SwiftUI.
El Problema del Centro
Normalmente, VStack centra sus contenidos. Pero si pones un Text dentro de un GeometryReader, el texto se irá a la esquina superior izquierda (0,0).
Solución: Debes posicionar manualmente los elementos dentro del reader o usar un contenedor que los centre.
GeometryReader { proxy in
Text("Hola Mundo")
.position(x: proxy.size.width / 2, y: proxy.size.height / 2)
}El Bucle de Renderizado (The Layout Loop)
Si usas GeometryReader para cambiar una @State variable que a su vez cambia el tamaño de la vista que contiene al GeometryReader, crearás un bucle infinito y la app se congelará o crasheará.
Regla de Oro: Evita actualizar @State directamente desde el cuerpo de un GeometryReader sin precauciones. Si necesitas pasar datos de tamaño hacia arriba en la jerarquía, usa PreferencesKey.
6. Técnica Avanzada: Leyendo tamaños sin alterar el Layout
A veces solo quieres saber cuánto mide un texto para dibujar un subrayado, pero no quieres que un GeometryReader expanda tu vista y arruine tu diseño.
La solución es usar GeometryReader en el background o overlay. Esta es una técnica pro.
struct TextoMedido: View {
@State private var textHeight: CGFloat = 0
var body: some View {
Text("Este es un texto dinámico")
.padding()
.background(
GeometryReader { proxy in
Color.clear // Invisible
.onAppear {
textHeight = proxy.size.height
}
.onChange(of: proxy.size) { newSize in
textHeight = newSize.height
}
}
)
Text("La altura del texto arriba es: \(textHeight)")
}
}Al ponerlo en el .background, el GeometryReader adopta el tamaño del Text (porque el background siempre iguala el tamaño de la vista), permitiéndonos leer sus dimensiones sin afectar el flujo visual.
7. Alternativas Modernas: ¿Sigue siendo necesario?
Con iOS 16 y versiones posteriores, Apple introdujo el protocolo Layout y la vista ViewThatFits.
- ViewThatFits: Permite elegir entre dos vistas dependiendo del espacio disponible, eliminando la necesidad de
GeometryReaderpara layouts adaptativos simples. - Layout Protocol: Permite crear contenedores personalizados con lógica matemática compleja mucho más eficientemente que un
GeometryReader.
Sin embargo, para animaciones basadas en scroll, dibujo de Paths complejos, y posicionamiento relativo exacto, GeometryReader sigue siendo el rey indiscutible.
Conclusión
GeometryReader es una navaja suiza. En manos inexpertas, puede cortar el flujo de tu diseño y causar dolores de cabeza con layouts rotos. Pero en manos expertas, es la herramienta que permite esas interfaces fluidas, vivas y perfectamente alineadas que caracterizan a las mejores apps del App Store.
Resumen de mejores prácticas:
- Úsalo solo cuando
VStack/HStack/Spacerno sean suficientes. - Recuerda que es “greedy” (codicioso) y llenará todo el espacio; limítalo con
.frame()si es necesario. - Ten cuidado con las coordenadas
.globalvs.local. - Para leer tamaños sin afectar el layout, úsalo en un
.background().
Ahora tienes el conocimiento. Abre Xcode y empieza a romper los límites del layout estático.
Si tienes cualquier duda sobre este artículo, contacta conmigo y estaré encantado de ayudarte 🙂. Puedes contactar conmigo en mi perfil de X o en mi perfil de Instagram










