How it works
A SymbolEffectTransition describes how an SF Symbol animates as it is added to, removed from, or swapped within the view tree. Rather than letting a symbol pop in and out abruptly, you supply a transition that the system runs whenever the symbol's content or identity changes, producing the fluid, physically-grounded motion that's characteristic of SF Symbols. Reach for it when a symbol's value is data-driven — a state toggle, a status indicator, a mode switch — and you want the change between two glyphs to read as a single continuous animation. You don't usually construct one directly; you select a named transition such as the replace effect and hand it to a transition-aware modifier.
Drive the symbol from state
A symbol transition only fires when the symbol actually changes, so the glyph must be derived from a value that mutates. Here the
Image(systemName:)resolves to either"wifi"or"wifi.slash"depending onisOn, and theButtonflipsisOnwithisOn.toggle()— each toggle gives the transition a new symbol to animate to.Attach the transition with contentTransition(.symbolEffect)
The transition is applied through the
.contentTransition(.symbolEffect(...))modifier, which tells SwiftUI to treat a change in the view's content as a symbol effect rather than a default cross-fade. This is the seam where SymbolEffectTransition plugs into theImage; without it, swapping the system name would simply replace one glyph with the next, no animation in between.Choose the effect: .replace
The argument to
.symbolEffectnames which transition to run. The.replaceeffect animates the outgoing glyph away while bringing the incoming glyph in, so"wifi.slash"morphs into"wifi"as one motion. Other transition styles (such as scale-based appear/disappear effects) are selected the same way — by passing a different effect value in this position.Layer ordinary styling around it
Because the transition operates on the symbol's content, it composes cleanly with the rest of the modifier chain. The
.font(.system(size: 60))sizing and the.foregroundStyle(isOn ? .blue : .gray)tint apply to whichever glyph is current, and the color change rides alongside the symbol replacement so both animate together whenisOnflips.
.contentTransition(.symbolEffect(.replace)) with .contentTransition(.symbolEffect(.replace.downUp)) and toggle the button to see the outgoing and incoming glyphs slide in opposite vertical directions instead of swapping in place.Example & preview
Press Run live & edit to compile it in your browser — then edit the Swift on the left and the preview re-renders live.
struct SymbolEffectTransitionDemo: View {
@State private var isOn = false
var body: some View {
VStack(spacing: 24) {
Image(systemName: isOn ? "wifi" : "wifi.slash")
.font(.system(size: 60))
.contentTransition(.symbolEffect(.replace))
.foregroundStyle(isOn ? .blue : .gray)
Button(isOn ? "Turn Off" : "Turn On") {
isOn.toggle()
}
.buttonStyle(.borderedProminent)
}
.padding()
}
}