Cómo agregar transiciones entre vistas usando MatchGeometryEffect en SwiftUI
Libranner Santos
29 noviembre, 20233min de lectura
Las animaciones y transiciones pueden mejorar la experiencia del usuario en nuestras aplicaciones. Una de las herramientas disponibles para agregar transiciones entre vistas es matchedGeometryEffect
.
¿Qué es MatchedGeometryEffect?
El modificador matchedGeometryEffect(id:in:properties:anchor:isSource:)
nos permite realizar transiciones entre vistas de manera sencilla.
Para poder utilizarlo, necesitamos un identificador (id
) único que debe ser el mismo para ambas vistas involucradas y un espacio de nombres (namespace
). Además, contamos con las propiedades (properties
) y anchor
con las cuales podemos personalizar más la animación, indicando, por ejemplo, si las propiedades a animar deben ser position
, frame
, o size
. En el caso de anchor
, podemos indicar la posición relativa de la vista utilizada.
Creando una transición
Veamos un ejemplo de cómo crear una transición usando matchedGeometryEffect
.
struct NumbersView: View {// 1@Namespace private var namespace@State private var numbers = [1, 2, 3]@State private var selectedNumber: Int?var body: some View {VStack {// 2HStack {ForEach(numbers, id: \.self) { number inText("\(number)").font(.system(size: 100))// 3.matchedGeometryEffect(id: number, in: namespace)}}.padding()}}}
Vamos a analizar qué hace este código:
- Declaramos las variables a utilizar.
namespace
se declara utilizando el property wrapper@Namespace
, lo usaremos para indicar a SwiftUI las vistas involucradas en la transición. Luego declaramosnumbers
, que tendrá un arreglo de números que mostraremos en la vista, y por último,selectedNumber
, que almacenará el valor del número seleccionado por el usuario. - Con este código, colocamos los números contenidos en
numbers
dentro de unHStack
. Creamos unText
para cada uno y le agregamos varios modificadores. - Agregamos el modificador
matchedGeometryEffect
usandonumber
como elid
e indicando elnamespace
.
Entre los modificadores, hay dos a los cuales debemos prestar atención. El primero es matchedGeometryEffect(id:in:properties:anchor:isSource:)
, con el cual configuramos la vista que formará parte de la transición. En este caso, le asignamos un id
, que es el mismo valor que number
, y pasamos una referencia a namespace
en el parámetro in
.
Hasta este punto, hemos configurado gran parte de nuestra aplicación. Falta poco. Justo debajo de .matchedGeometryEffect(id: number, in: namespace)
, agrega el siguiente código:
withAnimation {numbers.removeAll { $0 == number }selectedNumber = number}
Aquí colocamos la lógica para cuando el usuario haga el gesto tap sobre un número. Eliminamos el número en cuestión y asignaremos el valor de este a la variable selectedNumber
. Toda la lógica está dentro de withAnimation
, esto indica a SwiftUI que debe animar los cambios provocados por las variables de estado modificadas dentro del bloque.
Por último, justo después de .padding()
, agrega este código:
if let selectedNumber {Text("\(selectedNumber)").font(.system(size: 150)).fontWeight(.bold).foregroundStyle(.blue).matchedGeometryEffect(id: selectedNumber, in: namespace)}
Este código verifica si selectedNumber
no es nulo, en caso de que no lo sea, agregará a la jerarquía de vistas un Text
mostrando el contenido de selectedNumber
. También agregamos el .matchedGeometryEffect(id: selectedNumber, in: namespace)
para indicar que esta vista es parte de la transición. Dado que ambas vistas deben tener el mismo id
, en nuestro caso, el valor de selectedNumber
, SwiftUI podrá realizar la transición correctamente.
Ejecutemos el proyecto y seleccionemos los números para ver la transición en acción:
Cuando un número es seleccionado se hace una animación que simular el Text
moviendose hacia la nueva posición. Esto es gracias a matchedGeometryEffect
.