Animaciones de SFSymbols en SwiftUI

A partir de iOS 17 vamos a poder animar los iconos de SFSymbols que nos provee Apple tal como anunciaron en la WWDC23, usando el modificador symbolEffect(_:options:value:). En total son 7 animaciones y 4 comportamientos o behaviors de las animaciones.

Tipos de comportamientos de una animación

  1. Discrete:

Corresponde al protocolo DiscreteSymbolEffect, y solo reproduce una animación única al icono.

  1. Indefinite.

Corresponde al protocolo IndefiniteSymbolEffect, y reproduce la animación indefinidamente o hasta que se le indique.

  1. Transition.

Corresponde al protocolo TransitionSymbolEffect, crea una animación para que el icono aparezca o desaparezca de la vista.

  1. Content Transition.

Corresponde al protocolo ContentTransitionSymbolEffect, crea una animación para cambiar un icono por otro.

Animaciones de los iconos

  1. Bounce.

Su comportamiento es Discrete. Para reproducir esta animación, usamos el modificador .symbolEffect(.bounce, value: value) indicando .bounce como animación y un valor que indicará cuando ejecutar la animación. Veamos un ejemplo:

struct ContentView: View {
@State var cart: Int = 0
var body: some View {
VStack(spacing: 13) {
Image(systemName: "cart")
.symbolEffect(.bounce, value: cart)
.font(.system(size: 30))
Button("Añadir producto") {
cart += 1
}
}
}
}

Básicamente lo que estamos haciendo es animar el icono, cada vez que el valor de nuestro carrito, se ejecute la animación.

Ejemplo de una animación Bounce
  1. Pulse.

Su comportamiento puede ser tanto Discrete como Indefinite. Para reproducir esta animación, usamos el modificador .symbolEffect(.pulse, isActive: isActive) indicando .pulse como animación y también podemos indicar si la animación está activa o no, siendo este parámetro opcional, es decir, si no indicamos isActive el icono siempre estará animado. Veamos un ejemplo:

struct ContentView: View {
var body: some View {
VStack(spacing: 18) {
Image(systemName: "magnifyingglass")
.symbolEffect(.pulse)
.font(.system(size: 30))
}
}
}

En este caso no estamos indicando el valor isActive por lo que el icono estará animado siempre.

Ejemplo de una animación Pulse
  1. Variable Color.

Su comportamiento puede ser tanto Discrete como Indefinite. Para reproducir esta animación, usamos el modificador .symbolEffect(.variableColor, isActive: isActive) indicando .variableColor como animación y al igual que la animación anterior, podemos indicar si la animación está activa o no, siendo este parámetro opcional. Veamos un ejemplo:

struct ContentView: View {
var body: some View {
VStack(spacing: 18) {
Image(systemName: "shareplay")
.symbolEffect(.variableColor)
.font(.system(size: 30))
}
}
}
Ejemplo de una animación VariableColor
  1. Scale.

Su comportamiento es Indefinite. Para reproducir esta animación, usamos el modificador .symbolEffect(.scale, isActive: isActive) indicando .scale como animación y al igual que la animación anterior, podemos indicar si la animación está activa o no, siendo este parámetro opcional. Veamos un ejemplo:

struct ContentView: View {
@State var isActive = false
var body: some View {
VStack(spacing: 18) {
Image(systemName: "trash")
.symbolEffect(.scale.up, isActive: isActive)
.font(.system(size: 30))
Button("Animar") {
isActive.toggle()
}
}
}
}

Básicamente le estamos indicando que escale hacia arriba (es decir, que crezca) al pulsar el botón. También podemos indicar .scale.down si queremos el efecto contrario.

Ejemplo de una animación Scale
  1. Appear.

Su comportamiento puede ser tanto Transition como Indefinite. Para reproducir esta animación, usamos el modificador .symbolEffect(.appear, isActive: isActive) indicando .appear como animación e indicando si la animación está activa o no. Veamos un ejemplo:

struct ContentView: View {
@State var isActive = false
var body: some View {
VStack(spacing: 18) {
Image(systemName: "trash")
.symbolEffect(.appear, isActive: isActive)
.font(.system(size: 30))
Button("Animar") {
isActive.toggle()
}
}
}
}

Básicamente lo que estamos haciendo al pulsar el botón, es que el icono desaparecerá o aparecerá dependiendo del estado en el que esté, es decir, si el icono ya está presente, este desaparecerá al pulsar el botón, de lo contrario, aparecerá.

Ejemplo de una animación Appear
  1. Disappear.

Mismo comportamiento que la animación anterior (Appear), pero esta vez indicando .disappear . Veamos un ejemplo:

struct ContentView: View {
@State var isActive = false
var body: some View {
VStack(spacing: 18) {
Image(systemName: "trash")
.symbolEffect(.disappear, isActive: isActive)
.font(.system(size: 30))
Button("Animar") {
isActive.toggle()
}
}
}
}
Ejemplo de una animación Disappear
  1. Replace.

La última animación tiene un comportamiento de Content Transition. Para reproducir esta animación, usamos el modificador .contentTransition(.symbolEffect(.replace)). Veamos un ejemplo:

struct ContentView: View {
@State var isPaused = false
var body: some View {
VStack(spacing: 18) {
Button {
isPaused.toggle()
} label: {
Image(systemName: isPaused ? "pause.fill" : "play.fill")
.contentTransition(.symbolEffect(.replace))
.font(.system(size: 30))
}
}
}
}
Ejemplo de una animación Replace

Siguientes pasos con las animaciones de SFSymbols

  • Puedes configurar efectos muy específicos sin problemas, como por ejemplo: variableColor.iterative.hideInactiveLayers.
  • Xcode te ayuda a auto-completar cada parte del nombre, y en caso de que tengas algo incorrecto, ta dará un error en tiempo de compilación.
  • Recuerda que también puedes usar estos efectos en UIKit, usando la función addSymbolEffect.
let imageView: UIImageView = UIImageView(image: UIImage(systemName: "play.fill"))
imageView.addSymbolEffect(.variableColor.iterative)
  • Puedes quitar los efectos en tu imagen en UIKit usando la función removeSymbolEffect.
imageView.removeSymbolEffect(ofType: .variableColor)
  • El modificador symbolEffect se propaga a través de todas las vistas, veamos un ejemplo:
struct ContentView: View {
@State var isActive = true
var body: some View {
VStack(spacing: 18) {
Image(systemName: "trash")
.font(.system(size: 30))
Image(systemName: "play.fill")
.font(.system(size: 30))
Image(systemName: "pause.fill")
.font(.system(size: 30))
}
.symbolEffect(.pulse, isActive: isActive)
}
}
Ejemplo de una animación con symbolEffect

En este ejemplo, las tres imágenes tendrán la animación pulse.

  • Podemos evitar que una de las imágenes no tenga animación con el siguiente modificador: symbolEffectsRemoved().
Image(systemName: "trash")
.symbolEffectsRemoved()
  • Es posible combinar diferentes efectos:
struct ContentView: View {
var body: some View {
VStack(spacing: 18) {
Image(systemName: "trash")
.symbolEffect(.variableColor.iterative)
.symbolEffect(.scale.up)
.font(.system(size: 30))
}
}
}
Ejemplo de una animación combinada

Comparte este artículo

Subscríbete a nuestro Newsletter

Mantente al día en el mundo de las aplicaciones móviles con nuestro blog especializado.

Artículos semanales

Todas las semanas artículos nuevos sobre el mundo de las aplicaciones móviles.

No spam

No te enviaremos spam, solo contenido de calidad. Puedes darte de baja cuando quieras.

Contenido de calidad

Nada de contenido generado de manera automática usando ChatGPT.

Recomendaciones

Tips indispensables sobre mejores prácticas y metodologías.

© 2024 AsyncLearn