GeometryReader en SwiftUI en Español
GeometryReader en SwiftUI

GeometryReader en SwiftUI en Español

GeometryReader en SwiftUI es un contenedor de vistas como VStack, List, Form, etc pero con la diferencia que podemos acceder al GeometryProxy, un parámetro que nos servirá para crear custom layouts en las subvistas y añadir animaciones.

SwiftBeta
Aprende SwiftUI desde cero

GeometryReader es un contenedor de vistas en SwiftUI (como VStack, List, Form, etc) pero a diferencia de los demás podemos acceder a un parámetro llamado GeometryProxy que nos proporciona la posición y tamaño que ocupa la vista del GeometryReader. ¿Esto para qué nos sirve? Esta información es muy útil para posicionar las subvistas que añadimos a nuestro GeometryReader con un layout customizado, y también para crear animaciones.
Veremos unos ejemplos en el post de hoy.

GeometryReader en código

Vamos a usar código para ver esta vista en SwiftUI. Lo primero de todo es crear un proyecto de cero en Xcode y añadir dentro de ContentView un GeometryReader muy sencillo:

struct ContentView: View {
    var body: some View {
        GeometryReader { proxy in
            VStack {
                Text("Width: \(proxy.size.width)")
                    .background(Color.orange)
                    .padding()
                Text("Height: \(proxy.size.height)")
                    .background(Color.orange)
                    .padding()
            }
        }
        .background(Color.red)
    }
}
Código GeometryReader mostrando el size

Dentro de este GeometryReader hemos añadido un VStack con dos Texts. Fíjate que al usar el GeometryReader hay un parámetro llamado proxy de tipo GeometryProxy. ¿Qué es este proxy? Como hemos dicho, es un parámetro que nos proporciona información del GeometryReader en si, lo usamos dentro de los Text para mostrar el tamaño que ocupa este GeometryReader dentro del ContentView.
En este caso, el GeometryReader ocupa todo el ContentView (es el rectangulo que vemos con el color rojo). Tiene un width de 390 y height de 763.

GeometryReader mostrando su size

Dentro del parámetro proxy podemos extraer más información, como la coordenada donde está nuestro GeometryReader, SwiftUI calculará esta coordenada dependiendo del espacio de coordenadas que le indiquemos.
Es decir, podemos saber la coordenada relativa a la vista que lo contiene (en este caso es la misma que su vista padre ContentView) o también lo podemos saber a nivel de pantalla del device (son dos tipos de espacio de coordenadas distintos).

Para especificar el espacio de coordenadas que queremos, usaremos un método en el parámetro de tipo GeometryProxy llamado frame, este método acepta un enum, vamos a ver dos de sus cases para ver la sutil diferencia en este caso:

  • .local
  • .global
En el post de hoy entraremos más en detalle en el espacio de coordenadas global

Un pequeño apunte, hay que tener en cuenta que el sistema de coordenadas no es el mismo al que estábamos acostumbrados. En SwiftUI cuando añadimos una vista, esta aparece en la posición 0,0, que sería el centro de la pantalla. Pero en este caso, la posición 0,0 está en la esquina superior izquierda.

struct ContentView: View {
    var body: some View {
        GeometryReader { proxy in
            VStack {
                Text("Width: \(proxy.size.width)")
                    .background(Color.orange)
                    .padding()
                Text("Height: \(proxy.size.height)")
                    .background(Color.orange)
                    .padding()
                Text("Coordinates Local: \(proxy.frame(in: .local).debugDescription)")
                    .background(Color.orange)
                    .padding()
                Text("Coordinates Global: \(proxy.frame(in: .global).debugDescription)")
                    .background(Color.orange)
                    .padding()
            }
        }
        .background(Color.red)
    }
}
GeometryReader con dos espacios de coordenadas distintos

Hemos añadido dos Text mostrando la información del frame en local y en global, y el resultado es el siguiente:

En el espacio de coordenadas local el GeometryReader empieza en la coordenada x: o y:0 de nuestro ContentView. Pero las coordenadas globales (recordemos, la pantalla entera del device), el GeometryReader empieza en la coordenada x:0 y:47. Cosa que tiene todo el sentido, es decir, empieza en la posición y: 47 que es el height que hay desde la parte superior de la pantalla hasta que empieza el ContentView. En los dos casos tenemos el mismo tamaño (390 x 793).
Uno se basa en la posición a nivel de pantalla y el otro a nivel de vista padre.

Vamos hacer un pequeño cambio, vamos a cambiar el frame y vamos a entender mejor todo lo anterior:

struct ContentView: View {
    var body: some View {
        GeometryReader { proxy in
            VStack {
                Text("Width: \(proxy.size.width)")
                    .background(Color.orange)
                    .padding()
                Text("Height: \(proxy.size.height)")
                    .background(Color.orange)
                    .padding()
                Text("Coordinates Local: \(proxy.frame(in: .local).debugDescription)")
                    .background(Color.orange)
                    .padding()
                Text("Coordinates Global: \(proxy.frame(in: .global).debugDescription)")
                    .background(Color.orange)
                    .padding()
            }
        }
        .background(Color.red)
        .frame(width: 300, height: 300)
    }
}
Redimensionamos nuestro GeometryReader en SwiftUI

Al añadir en GeometryReader el modificador frame de 300 x 300 vemos como todos los valores han cambiado:

Ahora el tamaño es 300x300 (width y height de 300). Las coordenadas respecto a la vista que lo contiene son x:0 y:0 y las coordenadas respecto a la pantalla son x:45 y:278,5.

Vamos a ver algunos ejemplos prácticos para ver qué podemos hacer con el GeometryReader en nuestras apps.


Custom Layouts y GeometryReader

Ahora vamos a crear un Layout customizado, añadiendo 3 rectangulos en nuestra vista, 2 que ocupen la mitad de la pantalla del device y uno que esté debajo de los dos rectángulos y vaya de extremo a extremo de la pantalla. Para hacerlo usaremos el parámetro proxy de la vista GeometryReader.

El código sería el siguiente:

struct GeometryReaderStacks: View {
    var body: some View {
        VStack {
            GeometryReader { proxy in
                VStack(spacing: 0) {
                    HStack(spacing: 0) {
                        Rectangle()
                            .foregroundColor(Color.green)
                            .overlay(Text("1"))
                            .frame(width: proxy.size.width/2,
                                   height: proxy.size.height/2)
                        Rectangle()
                            .foregroundColor(Color.orange)
                            .overlay(Text("2"))
                            .frame(width: proxy.size.width/2,
                                   height: proxy.size.height/2)
                    }
                    Rectangle()
                        .foregroundColor(Color.purple)
                        .overlay(Text("3"))
                        .frame(width: proxy.size.width,
                               height: proxy.size.height * 0.33)
                }
            }
            .background(Color.red)
        }
    }
}
Custom Layout en SwiftUI con GeometryReader

y el resultado al compilar nuestra app es el siguiente:

El rectándulo rojo es el GeometryReader

GeometryReader y ScrollView

Vamos a ver otro ejemplo, esta vez vamos a animar las vistas de un ScrollView, a medida que hagamos scroll veremos como gracias al GeometryReader podemos añadir una animación.

Para ello, dentro de las vistas de nuestro ScrollView vamos a añadir un modificador llamado .rotation3DEffect

let arrayOfNames = ["Suscríbete a SwiftBeta",
                    "Aprende SwiftUI",
                    "Aprende Swift",
                    "Aprende Xcode",
                    "SwiftUI",
                    "Xcode",
                    "Swift"]

struct GeometryReaderScrollView: View {
    var body: some View {
        ScrollView(showsIndicators: false) {
            VStack {
                ForEach(arrayOfNames, id: \.self) { name in
                    GeometryReader { proxy in
                        VStack {
                            Text("\(proxy.frame(in: .global).minY)")
                            Spacer()
                            Text("\(name)")
                                .frame(width: 370, height: 200)
                                .background(Color.green)
                                .cornerRadius(20)
                            Spacer()
                        }
                        .shadow(color: .gray, radius: 10, x: 0, y: 0)
                        .rotation3DEffect(
                            Angle(degrees: Double(proxy.frame(in: .global).minY) - 47
                            ), axis: (x: 0.0, y: 10.0, z: 0.0)
                        )
                    }
                    .frame(width: 370, height: 300)
                }
            }
            .padding(.trailing)
            
        }
        .padding(.horizontal)
    }
}
Animación usando GeometryReader en SwiftUI

Y al compilar obtendríamos el siguiente resultado:

Lo que hemos visto hoy ha sido una pequeña introducción a GeometryReader y dos ejemplos en código para crear layouts customizados y animaciones.

Si quieres seguir aprendiendo sobre SwiftUI, Swift, Xcode, o cualquier tema relacionado con el ecosistema Apple


SwiftUI desde cero