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:
- Appear (
AppearSymbolEffect
) and Disappear (DisappearSymbolEffect
) - Bounce (
BounceSymbolEffect
) - Scale (
ScaleSymbolEffect
) - Variable Color (
VariableColorSymbolEffect
) - Pulse (
PulseSymbolEffect
) - Replace (
ReplaceSymbolEffect
)
Additionally, with the new SF Symbols 6 and iOS 18 three new animations were introduced:
- Wiggle (
WiggleSymbolEffect
) - Rotate (
RotateSymbolEffect
) - 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.
The animations mentioned beforehand are categorized based on four different behaviors:
- Discrete Behavior: Plays a one-off animation and then stops. This is referred to as
DiscreteSymbolEffect
protocol. - Indefinite Behavior: Continuously changes the symbol's aspects and maintains the changes indefinitely. This is referred to as
IndefiniteSymbolEffect
protocol. - Transition Behavior: Animates the symbol to bring it into view or out of view. This is referred to as
TransitionSymbolEffect
protocol. - 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.