The seventh revision of SF Symbols, the built-in collection of icons from Apple, was introduced at WWDC 2025. With these advancements, there’s more we can now do with SF Symbols as there are new APIs available, while the SF Symbols app was enhanced with capabilities to specify animations for our own icons. Leaving the latter aside, we’ll get to meet the most interesting additions that we can adopt to our apps.
Note: You can find an older detailed tutorial about SF Symbols here.
Animating with the drawOn and drawOff effects
Two new symbol effects are now available to use as of iOS 26, the DrawOn and its counterpart, DrawOff. They both animate the symbol’s appearance and disappearance respectively, with the difference in these two new effects being the fact that they use specific draw data; data that we can also set in the SF Symbols app in custom icons, but that’s not something we focus on in this post.
To see that working, let’s begin with the following Image view that presents a simple icon:
|
1 2 3 4 5 6 7 8 9 10 |
var image: some View { Image(systemName: "checkmark") .resizable() .scaledToFit() .frame(width: 80, height: 80) .foregroundStyle(.tint) } } |
The above Image view is resized and given a tint color for demonstration reasons.
We apply the two new draw effects using the symbolEffect(_:isActive:) modifier like so:
|
1 2 3 4 |
image .symbolEffect(.drawOn, isActive: isHidden) |
The isHidden argument is a state property that controls whether the effect is active:
|
1 2 3 |
@State private var isHidden = true |
isHidden is true at first, and that activates the drawOn effect. In this state the symbol is not drawn on screen initially, but when the value changes, the icon appears with the draw animation. Subsequent changes toggle the animation between the drawn-on and drawn-off states.
To complete the example, let’s add a button that toggles the isHidden property, having eventually this example view for now:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
struct ContentView: View { @State private var isHidden = true var body: some View { VStack(spacing: 40) { image .symbolEffect(.drawOn, isActive: isHidden) Button("Animate") { isHidden.toggle() } .buttonStyle(.borderedProminent) .padding(.top, 80) } } var image: some View { Image(systemName: "checkmark") .resizable() .scaledToFit() .frame(width: 80, height: 80) .foregroundStyle(.tint) } } |
The result of the drawOn effect is the following:
Once the drawOn effect animates the appearance of the symbol, the next toggle of the isHidden value makes SwiftUI play the opposite animation, which looks like the drawOff animation, even though we didn’t explicitly apply it.
You can play around and change the symbol effect to drawOff, making the isHidden initially false:
|
1 2 3 4 5 6 7 8 |
@State private var isHidden = false … image .symbolEffect(.drawOff, isActive: isHidden) |
Now the animation is the exact opposite; the icon appears by default, and it disappears on button tap with the drawOff effect. On the next tap, SwiftUI plays the reverse animation, making the symbol appear again.
Draw animation variations
The really interesting part with the draw symbol effects is not their addition to the already existing effects, but the fact that they provide animation variations that shine in multi-layer icons. There are three possible ways to animate, which we set right after the draw effect in code:
|
1 2 3 4 5 6 7 8 9 10 11 12 |
image .symbolEffect(.drawOn.wholeSymbol, isActive: isHidden) image .symbolEffect(.drawOn.byLayer, isActive: isHidden) image .symbolEffect(.drawOn.individually, isActive: isHidden) |
- Whole symbol: The entire symbol is animated as a single unit. This is similar to not providing any value after the draw effect.
- By layer: All layers start animating simultaneously.
- Individually: The animation of each layer starts right when the previous one finishes.
To see the result of all these, we’ll use an icon with multiple layers (e.g. “wifi”):
|
1 2 3 4 5 6 7 |
var image: some View { Image(systemName: "wifi") // … view modifiers … } } |
Note that the above draw variations have the same effect when icons have a single layer.
Gradient rendering mode
Along with the new draw symbol effects, SF Symbols 7 now support gradient as a new color rendering mode for icons. So far we were able to tint symbols, but only with a flat color. Now we can render with a gradient of the given tint color, using the symbolColorRenderingMode(_:) modifier with the .gradient argument:
|
1 2 3 4 |
heartImage .symbolColorRenderingMode(.gradient) |
For your reference, heartImage is the following:
|
1 2 3 4 5 6 7 8 9 10 |
var heartImage: some View { Image(systemName: "heart.fill") .resizable() .scaledToFit() .frame(width: 140, height: 140) .foregroundStyle(.pink) } } |
It’s easy to spot the difference between the gradient and flat color rendering mode by putting two icons side by side:
|
1 2 3 4 5 6 |
heartImage .symbolColorRenderingMode(.gradient) heartImage |
Here’s how they look like:

Wrapping up
We all use SF Symbols in our apps in order to present icons that users are familiar with. Now, we can make their appearance even more attractive, either by applying the draw effect, or just by applying a gradient tint color. I would recommend to download and explore the SF Symbols app for macOS, if you haven’t done that already. It’s a great tool that clearly demonstrates the possibilities of system icons. I hope you found this post useful. Thanks for reading!