Discover the New SFSymbols Animations Announced at WWDC24
You need to use Xcode 16 Beta or later and iOS 18 Beta or later to use the new animations.
Last year at WWDC23, Apple announced animations for SFSymbols. This year at WWDC24, three new animations were announced:
- Wiggle
- Rotate
- Breathe
Let’s look at how to use each of them.
Wiggle Animation
This animation allows us to highlight changes or points of action in our app. Symbols can animate left-to-right, top-to-bottom, clockwise, counterclockwise, or at a custom angle. Here’s an example:
var wiggleAnimation: some View {
VStack(spacing: 13) {
// 1
Image(systemName: "square.and.arrow.up")
.symbolEffect(.wiggle.up)
// 2
Image(systemName: "alarm.waves.left.and.right")
.symbolEffect(.wiggle.clockwise)
// 3
Image(systemName: "paperplane")
.symbolEffect(.wiggle.custom(angle: 315))
// 4
Image(systemName: "airplane")
.symbolEffect(.wiggle.left)
}
.font(.system(size: 30))
}
- We define that our animation goes from top to bottom. To achieve the opposite effect (bottom to top), use
.symbolEffect(.wiggle.down)
.
2. We define that our animation goes clockwise. The opposite effect is achieved with .symbolEffect(.wiggle.counterClockwise)
.
3. We define the angle at which we want the animation to occur. In this case, we use an angle of 315, but you can vary this angle as desired.
4. We define that our animation goes left to right. To achieve the opposite effect, use .symbolEffect(.wiggle.right)
.
In the example above, the animations run indefinitely. To make them execute in response to an action, you can do the following:
import SwiftUI
struct ContentView: View {
// 1
@State var animate = false
var body: some View {
VStack(spacing: 13) {
// 2
Image(systemName: "square.and.arrow.up")
.symbolEffect(.wiggle.up, value: animate)
.font(.system(size: 30))
Button {
// 3
animate.toggle()
} label: {
Text("Animate")
}
}
}
}
- We create a state variable called
animate
, which will be of typeBool
.
2. We use the initializer for symbolEffect
that allows us to pass our state variable as a parameter, which will trigger the animation when this variable changes.
3. We create a button whose action will toggle the state of our variable.
Rotate Animation
With .rotate
, symbols can animate clockwise, counterclockwise, or by layers. Here’s an example of each:
VStack(spacing: 13) {
// 1
Image(systemName: "airplane")
.symbolEffect(.rotate)
// 2
Image(systemName: "fan.desk")
.symbolEffect(.rotate.byLayer)
// 3
Image(systemName: "line.3.crossed.swirl.circle")
.symbolEffect(.rotate.counterClockwise)
// 4
Image(systemName: "gearshape.2")
.symbolEffect(.rotate.clockwise)
}
.font(.system(size: 30))
- Default animation, simply add
.symbolEffect(.rotate)
to get the animation.
2. Layer animation, useful for symbols with different layers, like “fan.desk”.
3. Counterclockwise animation.
4. Clockwise animation.
Like with the .wiggle
animation, you can start the animation in response to an action by passing our state variable: .symbolEffect(.rotate.byLayer, value: animate)
.
Breathe Animation
Finally, we have the .breathe
animation, which is very similar to the .pulse
animation, with the difference that .breathe
animates both the opacity and size of the symbol, while .pulse
only animates the opacity. Here’s an example of both to see the difference:
VStack(spacing: 13) {
// 1
Image(systemName: "heart")
.symbolEffect(.breathe)
// 2
Image(systemName: "heart")
.symbolEffect(.pulse)
}
.font(.system(size: 30))
- We see the
.breathe
animation.
2. We see the .pulse
animation.
In this type of animation, you can also pass the state variable .symbolEffect(.breathe, value: animate)
.