Sep 9, 2026

Transition or ContentTransition

SwiftUI Technique
Transition or ContentTransition

SwiftUI transition vs contentTransition: What Actually Changes?

Learn when to use transition and contentTransition in SwiftUI by asking one question: is the view entering or leaving, or is its content changing in place?

SwiftUI has two modifiers with remarkably similar names:

.transition(...).contentTransition(...)

They both describe how a change should look, and both need an animation to bring that change to life. But they solve two different problems.

Here is the distinction to remember:

Use transition when a view is inserted into or removed from the view hierarchy. Use contentTransition when a view stays in place but the content it displays changes.

That is the entire idea. Let’s make it visible.

The difference at a glance

What changed?BeforeAfterModifier
The view hierarchyNo Text viewA Text view existstransition
The content of an existing viewText("9")Text("10")contentTransition

transition: a view enters or leaves

Consider a view controlled by an if statement:

VStack {  Button(showMessage ? "Remove View" : "Insert View") {    withAnimation {      showMessage.toggle()    }  }  .buttonStyle(.bordered)    ZStack {      if showMessage {        Text("I enter and leave")          .font(.title3.bold())      }    }    .frame(height: 40)}

When showMessage becomes false, the Text view is removed. When it becomes true, the Text view is inserted again.

CleanShot 2026-08-03 at 11.30.32

This can be visually improved by adding a transition to the view that is being inserted or removed

if showMessage {    Text("Hello, SwiftUI!")        .font(.title)        .transition(.slide)}

The .slide transition describes how that insertion and removal should happen.

However, must also apply an animation to the state property that is triggering the animation

Button(showMessage ? "Remove View" : "Insert View") {  withAnimation {    showMessage.toggle()  }}

CleanShot 2026-08-03 at 11.40.40

There are several different transitions that you can apply

TransitionEffect
IdentityNo visual transition
OpacityFades in or out
SlideEnters from leading; exits toward trailing
MoveMoves to and from one specified edge
ScaleScales between nearly zero and full size
Custom scaleUses a custom scale and anchor
PushPushes the new and old content from an edge
OffsetMoves by a specified distance

Many of these transitions have additional parameters like edges, offsets and scales.

CleanShot 2026-08-03 at 13.49.17

There are two additional transitions that are rather special

Blur-replacement configurations

.transition(.blurReplace.transition(.blurReplace(.downUp)).transition(.blurReplace(.upUp))

The differences are very subtle

CleanShot 2026-08-03 at 14.00.38

Combined Transitions

You can also combine transitions like this

.transition(    .move(edge: .trailing)        .combined(with: .opacity))

CleanShot 2026-08-03 at 14.59.50

In every case, the important fact is not that some state changed. It is that the state change caused the view to enter or leave the hierarchy and the trigger must be within an animation block

If you enjoy my videos, I have a YouTube video on this topic as well

Mastering SwiftUI Transitions – Custom & Built-in Animations

https://www.youtube.com/watch?v=ZmdG0T_58wg

SF Symbol view-transition effects

As a special case of view transitions, there are now SFSymbol transitions too.

These transitions only affect views constructed by SFSymbols

.transition(.symbolEffect(.automatic)).transition(.symbolEffect(.appear)).transition(.symbolEffect(.disappear)).transition(.symbolEffect(.appear.up)).transition(.symbolEffect(.appear.down)).transition(.symbolEffect(.drawOn))   // iOS 26+.transition(.symbolEffect(.drawOff))  // iOS 26+

CleanShot 2026-08-03 at 14.16.08

You can do much more with SFSymbol effect transitions and encourage you to watch my YouTube video on that topic.

SFSymbol Animations in iOS 17

https://www.youtube.com/watch?v=euia2GkPo7U

contentTransition: the view stays, its content changes

Now consider a counter:

struct CounterExample: View {  @State private var count = 0    var body: some View {    VStack(spacing: 20) {      Text(count, format: .number)        .font(.system(size: 64, weight: .bold))      HStack {        Button("Decrease") {          withAnimation {            count -= 1          }        }        Button("Increase") {          count += 1        }      }    }  }}

When you tap on Decrease or Increase the state value changes and the view updates. It is not moving in our out of the view, but the content is changing.

CleanShot 2026-08-03 at 15.17.45

However, the transition from one number to another is not very exciting. We can improve this by applying a contentTransition. Like a transition it too requires two parts. A contentTransition modifier and an animation block surrounding the trigger.

Text(count, format: .number)    .font(.system(size: 64, weight: .bold))    .contentTransition(        .numericText(value: Double(count))    )

And

Button("Decrease") {    withAnimation {        count -= 1    }}Button("Increase") {    withAnimation {        count += 1    }}

CleanShot 2026-08-03 at 15.19.12

In this case, we used a numericText content transition where it is observing the value of the count variable which has to be cast as a Double.

SwiftUI provides five main content-transition families, with two forms of numericText.

Content transitionPurpose
identityMakes the content change immediately without a visual transition
opacityCross-fades between the old and new content
interpolateInterpolates matching text glyphs and their visual properties
numericTextAnimates changing numeric text vertically
symbolEffectAnimates between SF Symbols or symbol configurations

Sample content transitions

Once you know that the content is changing in place, SwiftUI offers several useful choices.

Numeric text

Text(count, format: .number)    .contentTransition(.numericText(value: Double(count)))

This is ideal for counters, scores, totals, timers, and changing measurements.

Opacity

Image(systemName: isDaytime ? "sun.max.fill" : "moon.fill")    .contentTransition(.opacity)

This gives changing text or images a simple cross-fade.

Interpolation

Text("Tap to animate")    .font(.system(size: emphasized ? 40 : 32))    .foregroundStyle(emphasized ? .blue : .red)    .contentTransition(.interpolate)

When the text contains matching glyphs, interpolation can animate properties such as size, position, and color.

Symbol replacement

Image(systemName: isLiked ? "heart.fill" : "heart")    .contentTransition(.symbolEffect(.replace))

This is designed for changes between compatible SF Symbols.

CleanShot 2026-08-03 at 15.27.18

A simple decision test

When you are unsure which modifier to use, look at the code that responds to the state change.

Ask these questions in order:

  1. Does an if, switch, collection update, or identity change cause a view to appear or disappear?

Start with transition.

  1. Does the same Text, Image, or other view remain while its displayed content changes?

Start with contentTransition.

  1. Did you provide an animation for the state change?

If not, neither transition will have an animated change to perform.

Final takeaway

The names are similar, but the jobs are not:

View enters or leaves       → transitionView stays, content changes → contentTransition

Once you focus on what actually changed—the hierarchy or the content—the choice becomes straightforward.

And as with all animation, restraint helps. A transition should clarify a change, not compete with it.