Animating SF Symbols with the symbol effect modifier

Animating SF Symbols with the symbol effect modifier

Explore the different ways you can animate SF Symbols with SwiftUI

One of the latest enhancements Apple introduced for SF Symbols is the ability to play dynamic animations and visual effects using the symbolEffect(_:) modifier. For example, when connecting to Wi-Fi or mirroring a screen, these subtle animations help users understand the current status or progress of the action.

In this guide, we will see the various types of animations available and show how to implement them in our SF Symbols

Animations Available

With the symbolEffect(_:options:value:) modifier we can use the following types of animations:

  1. Appear (AppearSymbolEffect) and Disappear (DisappearSymbolEffect)
  2. Bounce (BounceSymbolEffect)
  3. Scale (ScaleSymbolEffect)
  4. Variable Color (VariableColorSymbolEffect)
  5. Pulse (PulseSymbolEffect)
  6. Replace (ReplaceSymbolEffect)

Additionally, with the new SF Symbols 6 and iOS 18 three new animations were introduced:

  1. Wiggle (WiggleSymbolEffect)
  2. Rotate (RotateSymbolEffect)
  3. Breath (BreatheSymbolEffect)

One thing that is important to notice is that even though the modifiers responsible for the symbol effects are present in the SwiftUI framework, the protocols and implemented animations for SF Symbols are part of the Symbols framework.

Symbols | Apple Developer Documentation
Apply universal animations to symbol-based images.

The animations mentioned beforehand are categorized based on four different behaviors:

  1. Discrete Behavior: Plays a one-off animation and then stops. This is referred to as DiscreteSymbolEffect protocol.
  2. Indefinite Behavior: Continuously changes the symbol's aspects and maintains the changes indefinitely. This is referred to as IndefiniteSymbolEffect protocol.
  3. Transition Behavior: Animates the symbol to bring it into view or out of view. This is referred to as TransitionSymbolEffect protocol.
  4. Content Transition Behavior: Animates the transition from one symbol to another. This is referred to as ContentTransitionSymbolEffect protocol.

Animations with a discrete behavior

When we need to include simple animation that needs to be performed just one time we can use animations with a discrete behaviour. This type of animation can be performed using the symbolEffect(_:) modifier and a trigger value that conforms to the Equatable protocol. Once the trigger changes, the animation will play.

Discrete behavior animations include bounce, pulse, and variable color.

import SwiftUI

struct ContentView: View {
    @State private var isActive = false

    var body: some View {
        Image(systemName: "swift")
            .font(.system(size: 100))
            
            .symbolEffect(.bounce, value: isActive)
            .onTapGesture {
                isActive.toggle()
            }
    }
}

In this example, tapping the symbol toggles the isActive state, which in turn triggers the pulse symbol effect, making it pulse.

Animations with an indefinite behavior

When you want an animation to keep playing indefinitely, you can use animations with indefinite behavior. These types of animations can be performed using the symbolEffect(_:) modifier, but this time we need a boolean value as well.

When the boolean value is set to true, the animation will play until it is disabled or removed. Once the boolean value becomes false it will stop animating.

Animations with this behavior include pulse, variable color, scale, appearance, and disappearance.

import SwiftUI

struct ContentView: View {
    @State private var isActive = false

    var body: some View {
        Image(systemName: "swift")
            .font(.system(size: 100))
            .foregroundColor(isActive ? .blue : .black)
            
            .symbolEffect(.scale.up, isActive: isActive)
            .onTapGesture {
                isActive.toggle()
            }
    }
}

Animations with a transition behavior

When we want to show or hide an SF Symbol within our View, we can use an animation with a transition behavior. To achieve this, we just need to apply the .transition(_:) modifier to the SF Symbol defining the type of transition as a parameter.

import SwiftUI

struct ContentView: View {
    @State private var isPresented = false

    var body: some View {
        if isPresented {
            Image(systemName: "swift")
                .font(.system(size: 100))
                .transition(.symbolEffect(.appear))
        } else {
            Button("Show symbol", action: { isPresented.toggle() })
        }
    }
}

Animations with a content transition behavior

In certain situations, there may be a need to transition between different symbols, such as when we authenticate with FaceID and then see the checkmark symbol. To achieve this kind of effect, we can use the contentTransition(_:) modifier along with the replacement animation.

import SwiftUI

struct ContentView: View {
    @State private var isPresented = false
    
    var body: some View {
        VStack(spacing: 50){
            Image(systemName: isPresented ? "checkmark.circle.fill" : "faceid")
                .font(.system(size: 100))
                .contentTransition(.symbolEffect(.replace))
            
            Button("Show symbol", action: { isPresented.toggle() })
        }
    }
}

Starting from SF Symbol 6 the replace content transition behaves as the famously known "Magic Move" effect. It presents a smart transition between two symbols with related shapes.

Customizing animation

You can further customize the symbolEffect(_:) modifier by using the options parameter. This parameter allows you to specify for example how many times the animation needs to be played.

import SwiftUI

struct ContentView: View {
    @State private var isActive = false
    var body: some View {
        
        Image(systemName: "swift")
            .font(.system(size: 100))
            
            .symbolEffect(.pulse, options: .repeat(3), value: isActive)
            .onTapGesture {
                isActive.toggle()
            }
        
    }
}

In this example, the pulse effect has been customized to repeat 3 times.