Why Your SwiftUI Animations Still Feel Off (Even When They Shouldn’t)
Published March 31, 2026 · Jordan Kim
SwiftUI animations have come a long way, but even in well-structured apps they can still feel slightly off in ways that are hard to explain. Not always broken. Not always janky enough to file a radar. Just… a little wrong.
TL;DR
- Some SwiftUI animations feel rough not because the animation itself is wrong, but because layout, state updates, and view identity are fighting each other
- Implicit animations are convenient, but they can hide what is actually being animated and when
- View invalidation and container behavior still create edge cases that are easy to miss
- If an animation feels off, the problem is often structural rather than visual
It’s Not Just the Animation
One of the easiest mistakes to make in SwiftUI is assuming that animation quality mostly comes down to duration and easing. In practice, animation is tightly coupled to layout, state changes, and the timing of view updates.
That means an animation can technically be configured correctly and still feel strange because something else in the update cycle is changing at the same time.
A view shifting size, a parent recalculating layout, or a list re-evaluating identity can all make a perfectly reasonable transition feel less smooth than expected.
Why Implicit Animation Still Bites People
Implicit animation is one of SwiftUI’s nicest features when it works well. But it can also make it harder to see what the framework thinks is animating.
Consider something like:
.animation(.spring(), value: isExpanded)
At a glance, that reads cleanly. But in real views, isExpanded might affect multiple properties at once: padding, frame, opacity, alignment, clipping, and even whether child content exists at all.
When that happens, the issue usually isn’t “SwiftUI animation is bad.” It’s that the state change is driving more view churn than the animation was designed to hide.
Layout Changes Are Often the Real Culprit
The most common source of awkward-feeling animation is layout invalidation. If a child view changes and that causes its parent stack to resize or reflow, the “animation” the user sees is often a mix of transition and layout correction.
For example:
if isExpanded {
DetailsView()
.transition(.move(edge: .top).combined(with: .opacity))
}
This can look great in isolation. But if DetailsView() changes the overall height of a card inside a scrolling container, the surrounding layout may shift in a way that feels “laggy” even when the transition itself is working.
That’s why animation issues so often get misdiagnosed. The visible symptom is motion, but the underlying problem is layout.
View Identity Makes a Bigger Difference Than People Expect
Another subtle source of bad-feeling animation is identity. If SwiftUI thinks one view disappeared and a new view was inserted, it may animate that update very differently from a simple state-driven change.
This is especially easy to run into with conditional branches, dynamic lists, and views whose shape changes significantly depending on state.
if isSelected {
SelectedRowView(item: item)
} else {
DefaultRowView(item: item)
}
That might look harmless, but from SwiftUI’s perspective, that can be a full replacement rather than one view updating in place. And that changes how animation behaves.
Why Lists and Scrolling Containers Make Everything Harder
Animations that feel acceptable in a static container often start to feel much rougher inside List, LazyVStack, and other scrolling views.
- cells may be reused or re-evaluated more aggressively than expected
- height changes can create visible snapping around the animated element
- scroll position and animation timing can compete with each other
- seemingly small state changes can ripple through much larger portions of the hierarchy
This is one reason animation polish often feels harder in production than in demos. Real containers are doing much more work than the animation API surface suggests.
What Usually Helps
The best improvements usually come from making updates smaller and more predictable, not from endlessly tweaking spring values.
- separate structural layout changes from purely visual changes when possible
- prefer stable identity when transitioning between similar states
- be careful about animating views whose parent layout is also changing dramatically
- test animations inside the actual production container, not just isolated previews
In other words: if something feels off, zoom out. The fix may not be inside the animation modifier at all.
The Broader Pattern
The biggest SwiftUI animation frustrations usually come from asking one state change to do too much. Animate insertion, animate opacity, animate layout, preserve scroll behavior, keep identity stable, and handle accessibility — all at the same time.
SwiftUI can absolutely produce polished motion. But it rewards clarity in structure more than cleverness in modifiers.
Final Thoughts
If your animations still feel slightly off even after tuning curves and durations, you’re probably not imagining it.
More often than not, the issue is not that SwiftUI failed to animate. It’s that the view hierarchy is asking the framework to solve several different motion problems at once.
And once you start looking at animation as a layout and identity problem — not just a timing problem — a lot of those weird edge cases start to make much more sense.