Programación en Swift y SwiftUI para iOS Developers

Animaciones en SwiftUI: Animaciones explicitas e implicitas

Las animaciones en SwiftUI son automáticas y mágicas. Tienes que definir dos estados de una vista y SwiftUI se encargará del resto, animando los cambios entre dos estados. También te permite animar los cambios para vistas individuales y las transiciones entre vistas. El framework viene con una serie de animaciones integradas para crear diferentes efectos.

En este artículo aprenderemos como animar vistas usando animaciones implícitas y explícitas en SwiftUI.

Animaciones explicitas e implicitas

Swift ofrece dos tipos de animaciones, implícitas y explícitas. Ambas permiten animar vistas y transiciones de vistas. Para implementar animaciones implícitas el framework ofrece un modifier llamado animation. Adjuntas este modifier a las vistas que quieres animar y especificas el tipo de animación que prefieras. Opcionalmente, puedes definir la duración y el retraso de la animación. SwiftUI luego renderizará automáticamente la animación en función de los cambios de estado de las vistas.

Muchas de los tipos de vista incluidos en SwiftUI contienen propiedades que controlan la apariencia de la vista como scale, opacity, color o rotation angle. Las propiedades de este tipo son animables, en que el cambio de un estado de propiedad a otro puede ser animado en lugar de ocurrir instantáneamente.

Las animaciones implícitas usando el modifier animation() implementan una animación de cualquiera de las propiedades animables en una vista antes del modifier animation.

Las animaciones explicitas ofrecen un control más preciso sobre las animaciones que quieres presentar. En lugar de adjuntar un modifier a la vista, le dices a SwiftUI que cambios de estado quieres animar dentro del bloque withAnimation().

Animaciones Implicitas

Para entender como podemos crear una animación implícita vamos a verlo con un ejemplo:

Abrimos XCode y creamos un nuevo proyecto, seleccionando como lenguaje de programación Swift y como interfaz la de SwiftUI.

En este ejemplo vamos a crear la siguiente animación que puedes ver en las imágenes de antes y después:

Es una simple figura tocable que consiste en un MacBook y un círculo. Cuando un usuario toca el MacBook o círculo, el círculo cambia de color y el color del MacBook cambia a azul. Además el MacBook incrementa su tamaño. De manera que tenemos diferentes estados:

-El color del círculo cambia de azul a gris.

-El color del MacBook cambia de blanco a azul.

-El tamaño del MacBook se incrementa.

Para implementar este círculo usando SwiftUI, añade el siguiente código a ContentView.swift:

struct ContentView: View {
    
    @State private var circleColorChanged: Bool = false
    @State private var macbookColorChanged: Bool = false
    @State private var macbookSizeChanged: Bool = false
    
    var body: some View {
        
        ZStack {
            
            Circle()
                .frame(width: 200, height: 200)
                .foregroundStyle(circleColorChanged ? Color(.systemGray5) : .blue)
                
            Image(systemName: "macbook")
                .foregroundStyle(macbookColorChanged ? .blue : .white)
                .font(.system(size: 100))
                .scaleEffect(macbookSizeChanged ? 1.0 : 0.5)
                
            
        }
               
        .onTapGesture {
            circleColorChanged.toggle()
            macbookColorChanged.toggle()
            macbookSizeChanged.toggle()
        }
      
        
    }
}

En este código en lenguaje de programación Swift, definimos tres variables de estado para representar los estados del color del círculo, el color del MacBook y el tamaño del MacBook, con el valor inicial puesto a falso.

Usamos una ZStack para superposicionar la imagen del MacBook en la parte superior del círculo. SwiftUI viene con el onTapGesture modifier para detectar gestos de tap por parte del usuario, el cual puede ser añadido para hacer cualquier vista tocable.

En el closure onTapGesture, alternamos las variables de estado para cambiar la apariencia de la vista.

En el preview de XCode puedes ver como los cambios:

Pero estos cambios no están animados. Para animar los cambios usamos el modifier animation. SwiftUI monitorea los cambios de valor especificados en el modifier animation. Cuando un valor cambia, calcula y renderiza la animación, lo que permite que las vistas pasen sin problemas de un estado a otro. Si tocas el MacBook nuevamente, deberías ver una animación elegante.

El modifier animation se puede aplicar no solo a una sola vista, sino también a un grupo de vistas. Para crear esta animación implícita vamos a añadir el modifier animation a la ZStack de esta manera, el código sería el siguiente:

struct ContentView: View {
    
    @State private var circleColorChanged: Bool = false
    @State private var macbookColorChanged: Bool = false
    @State private var macbookSizeChanged: Bool = false
    
    var body: some View {
        
        ZStack {
            
            Circle()
                .frame(width: 200, height: 200)
                .foregroundStyle(circleColorChanged ? Color(.systemGray5) : .blue)
                
            Image(systemName: "macbook")
                .foregroundStyle(macbookColorChanged ? .blue : .white)
                .font(.system(size: 100))
                .scaleEffect(macbookSizeChanged ? 1.0 : 0.5)
                
            
        }
        .animation(.default, value: circleColorChanged)
        .animation(.default, value: macbookSizeChanged)
        
        .onTapGesture {
            circleColorChanged.toggle()
            macbookColorChanged.toggle()
            macbookSizeChanged.toggle()
        }
      
        
    }
}

En el ejemplo, usamos la animación predeterminada, que es la animación spring en iOS. SwiftUI también ofrece una variedad de animaciones integradas para elegir, incluidas las animaciones lineales, easeIn, easeOut y easeInOut.

La animación lineal anima los cambios a una velocidad constante, mientras que las animaciones de aceleración tienen distintas velocidades y curvas de aceleración.

Para usar una animación alternativa, solo tienes que configurar la animación específica en el modificador de animación. Supongamos que quieres usar la animación lineal; puedes cambiar .default por lo siguiente:

.animation(.linear, value: circleColorChanged)

Para personalizar la animación spring, puedes llamar a la función spring y especificar los parámetros de la animación, como la duración y el tipo de rebote:

.animation(.spring(.bouncy, blendDuration: 1.0), value: circleColorChanged)

Esto genera una animación basada en spring que le da al MacBook del ejemplo un efecto irregular. Al ajustar estos parámetros, se pueden crear diferentes efectos de animación

Animaciones Explicitas

Así es como se animan las vistas usando animación implícita. Veamos cómo podemos lograr el mismo resultado usando animación explícita. Como se explicó antes, es necesario envolver los cambios de estado en un bloque withAnimation. Para crear el mismo efecto animado, puedes escribir el código de esta manera: 

struct ContentView: View {
    
    @State private var circleColorChanged: Bool = false
    @State private var macbookColorChanged: Bool = false
    @State private var macbookSizeChanged: Bool = false
    
    var body: some View {
        
        ZStack {
            
            Circle()
                .frame(width: 200, height: 200)
                .foregroundStyle(circleColorChanged ? Color(.systemGray5) : .blue)
                
            Image(systemName: "macbook")
                .foregroundStyle(macbookColorChanged ? .blue : .white)
                .font(.system(size: 100))
                .scaleEffect(macbookSizeChanged ? 1.0 : 0.5)
                
            
        }
        
        .onTapGesture {
            withAnimation(.default){
                circleColorChanged.toggle()
                macbookColorChanged.toggle()
                macbookSizeChanged.toggle()
                
            }
            
        }
      
        
    }
}

Ya no usamos el modifier de animación; en su lugar, envolvemos el código dentro de onTapGesture con withAnimation. La función withAnimation retoma un parámetro de animación, donde especificamos el tipo de animación que queremos usar.

Para usar una animación de spring puedes actualizar el código de esta manera:

.onTapGesture {
            withAnimation(.spring(.bouncy, blendDuration: 1.0)){
                circleColorChanged.toggle()
                macbookColorChanged.toggle()
                macbookSizeChanged.toggle()
                
            }
            
        }

Con la animación explícita, tienes un control preciso sobre los cambios de estado que quieres animar. Si no quieres animar el cambio de tamaño del icono del corazón, puedes excluir esa línea de código del bloque withAnimation:

.onTapGesture {
            withAnimation(.spring(.bouncy, blendDuration: 1.0)){
                circleColorChanged.toggle()
                macbookColorChanged.toggle()
               
                
            }
				macbookSizeChanged.toggle()
            
        }

En este caso, SwiftUI excluye la animación de escala y solo anima los cambios de color.

Quizás te preguntes si podemos desactivar la animación de escala mediante la animación implícita. ¡Puedes! Puedes reordenar el modifier .animation para evitar que SwiftUI anime un determinado cambio de estado.

Al reordenar el modificador .animation, puedes lograr el efecto deseado. Aquí está el código modificado: 

struct ContentView: View {
    
    @State private var circleColorChanged: Bool = false
    @State private var macbookColorChanged: Bool = false
    @State private var macbookSizeChanged: Bool = false
    
    var body: some View {
        
        ZStack {
            
            Circle()
                .frame(width: 200, height: 200)
                .foregroundStyle(circleColorChanged ? Color(.systemGray5) : .blue)
                .animation(.spring(.bouncy, blendDuration: 1.0), value: circleColorChanged)
            Image(systemName: "macbook")
                .foregroundStyle(macbookColorChanged ? .blue : .white)
                .font(.system(size: 100))
                .animation(.spring(.bouncy, blendDuration: 1.0), value: macbookColorChanged)
                .scaleEffect(macbookSizeChanged ? 1.0 : 0.5)
                
            
        }
        
        .onTapGesture {
            
                circleColorChanged.toggle()
                macbookColorChanged.toggle()
                macbookSizeChanged.toggle()
            
        }
      
        
    }
}

Para la vista de imagen, colocamos el modifier animation justo antes de scaleEffect. Esto deshabilitará la animación. SwiftUI solo animará los cambios de color y excluirá la animación de escala.

Si bien puedes lograr la misma animación usando animación implícita, en mi opinión, es más conveniente usar animación explícita en este caso.

Leave a Reply

Your email address will not be published. Required fields are marked *

Previous Article

Que es @Binding en SwiftUI

Next Article

Como crear una tabla en SwiftUI

Related Posts