Swift and SwiftUI tutorials for Swift Developers

Implicit vs Explicit Animation in SwiftUI

As an iOS Developer, you know that a user interface must be not only functional but also fluid, engaging, and communicative. In the Apple ecosystem, movement and transitions are a fundamental part of the design language. Since the introduction of SwiftUI, the way developers approach animations has changed drastically, moving from the imperative manipulation of views in UIKit to a completely declarative and state-based approach.

However, when diving into modern Swift programming with this framework, one of the most common and fundamental questions arises: What is the real difference between Implicit vs Explicit Animation in SwiftUI

In this extensive tutorial, we will break down everything from the most basic concepts to the most advanced implementations so you can master both techniques, optimize your applications’ performance, and avoid the dreaded visual side effects that occur when state mutates unexpectedly.


1. The Animation Paradigm in SwiftUI

Before diving into the Swift code, it is crucial to understand how SwiftUI thinks. Unlike UIKit or AppKit, where you tell the system how to animate something (for example, “move this view from X to Y over 0.3 seconds”), in SwiftUI you declare what should happen when the state changes.

SwiftUI observes state variables (@State, @Binding, @ObservableObject, etc.). When one of these values changes, the framework recalculates the views that depend on it and automatically interpolates the differences between the old and new view. This interpolation is what we perceive as an animation.

The exact place where we tell SwiftUI to perform this interpolation defines whether we are using an implicit or explicit animation.


2. Implicit Animations

Implicit animations are those applied directly to a view via a modifier. They tell SwiftUI: “Hey, if any of the animatable properties of this view (or its child views) change for any reason, I want you to animate that change.”

How do they work?

Traditionally, this was achieved by adding the .animation() modifier at the end of a view’s modifier chain.

Let’s look at a practical example. Imagine you want to create a button that scales up when tapped:

import SwiftUI

struct ImplicitAnimationView: View {
    @State private var isScaled: Bool = false

    var body: some View {
        Button(action: {
            isScaled.toggle() // We change the state
        }) {
            Text("Implicit Animation")
                .font(.headline)
                .padding()
                .foregroundColor(.white)
                .background(Color.blue)
                .cornerRadius(10)
        }
        .scaleEffect(isScaled ? 1.5 : 1.0)
        // Implicit animation modifier (iOS 15+)
        .animation(.easeInOut(duration: 0.5), value: isScaled)
    }
}

Code Explanation

In this Xcode snippet:

  1. We have a state @State private var isScaled.
  2. The button changes this state via isScaled.toggle().
  3. The .scaleEffect modifier reacts to this state.
  4. The .animation(.easeInOut(duration: 0.5), value: isScaled) modifier is the key.

Important note for the modern iOS Developer: Starting with iOS 15, macOS 12, and watchOS 8, Apple deprecated the .animation(_:) modifier without the value parameter. The new standard requires you to associate the implicit animation with a specific value that conforms to the Equatable protocol. This solves the biggest historical problem with implicit animations: animating things by accident. Now, the view will only animate if the state change was triggered by the isScaled variable.

Advantages of Implicit Animation

  • Ease of use: It is extremely easy to implement. Adding a single line of code can bring a complex component to life.
  • Modularity: You can encapsulate animations within UI components (custom views) without worrying about how the state will be changed from the outside.
  • Composition: You can have multiple animation modifiers listening to different values in the same view hierarchy.

Disadvantages

  • Less control: The modifier applies the animation to all changes occurring in that view that depend on the observed value, which can sometimes lead to unexpected behaviors if you have a very complex view.

3. Explicit Animations

Explicit animations take a different approach. Instead of attaching the animation instruction to the view, you attach the animation instruction to the state mutation itself.

You tell SwiftUI: “I’m going to change this state variable right now, and I want any view in the entire application affected by this particular change to animate.”

How do they work?

To achieve this, we wrap the state change inside a global function block called withAnimation.

Let’s look at the same button example, but this time using an explicit animation in Swift:

import SwiftUI

struct ExplicitAnimationView: View {
    @State private var isScaled: Bool = false
    @State private var buttonColor: Color = .blue

    var body: some View {
        Button(action: {
            // Explicit Animation
            withAnimation(.spring(response: 0.4, dampingFraction: 0.6)) {
                isScaled.toggle()
                buttonColor = isScaled ? .green : .blue
            }
        }) {
            Text("Explicit Animation")
                .font(.headline)
                .padding()
                .foregroundColor(.white)
                .background(buttonColor)
                .cornerRadius(10)
                .scaleEffect(isScaled ? 1.5 : 1.0)
        }
        // Note: No .animation() modifier here!
    }
}

Code Explanation

  1. Observe the action block of the Button.
  2. We use withAnimation(.spring(...)) { ... }.
  3. Inside the block, we modify isScaled and buttonColor.
  4. The view does not have any .animation() modifier.

When SwiftUI executes this block, it knows that the variables isScaled and buttonColor were modified in an animated way (specifically using a spring). Then, it searches the view hierarchy for any component that depends on those variables and animates its transition from its current state to the new one.

Advantages of Explicit Animation

  • Surgical Precision: You have absolute control over when and why an animation occurs. Only the state changes within the withAnimation block will be animated.
  • Synchronization: It is ideal for animating multiple properties and multiple disconnected views on the screen at the same time. By changing the state centrally, all affected views react in sync.
  • Prevents visual collisions: If a state changes due to a background update (e.g., data received from the network), you don’t want the interface to jump with a sudden animation, unless you use withAnimation.

Disadvantages

  • Coupling logic and UI: In strict architectures like MVVM or TCA (The Composable Architecture), it is sometimes awkward to place the withAnimation logic inside the ViewModel, mixing user intent with pure presentation rules.

4. The Verdict: Implicit vs Explicit Animation in SwiftUI

We’ve reached the core point for any iOS Developer. Understanding the theoretical difference is fine, but how do you decide which one to use in your day-to-day work in Xcode?

Here is the golden rule in Swift programming for declarative interfaces:

Use explicit animations (withAnimation) whenever the animation is the direct result of a user action. Use implicit animations (.animation(_:value:)) when the animation is an inherent and independent property of a visual component.

Ideal Use Cases

When to choose Explicit Animation:

  1. Button Taps and Gestural Interactions: When the user taps a “Submit” button, toggles a switch, or drags a card, that user action is what should trigger the global animation.
  2. Full Screen Transitions: Presenting a sheet, hiding a side menu, or switching between custom tabs.
  3. Complex State Updates: If a block of logic changes five state variables at once and they all need to animate together, withAnimation is the way to go.

When to choose Implicit Animation:

  1. Low-level UI components: If you are building a custom loading indicator (a spinner), you want it to continuously spin based on an internal variable, regardless of how the rest of the app interacts with it.
  2. Reactions to asynchronous data streams: If a value is constantly updating from a sensor (like in watchOS) and you simply want a progress bar to smoothly slide to the new value instead of jumping abruptly.

5. Optimizing for the Apple Ecosystem: iOS, macOS, and watchOS

One of the marvels of SwiftUI is its cross-platform capability. However, as a senior iOS Developer, you must know that hardware and user expectations vary greatly across devices.

watchOS: Performance and Battery

The Apple Watch has more limited resources. In watchOS, excessive use of complex animations (like animated blurs or dynamic shadows) can drain the battery and cause stuttering (dropped frames).

  • Tip: In watchOS, prefer simple and direct explicit animations (like opacity and scale). Implicit animations on long dynamic lists can cause performance issues if you don’t bind the value parameter correctly.

macOS: Large Screens and Cursors

On Mac, interactions are usually handled by the mouse rather than touch. States like .onHover are vital.

  • Tip: Implicit animations shine on macOS for hover states. You can add an .animation(.default, value: isHovering) to subtly light up buttons or change typography size when hovering, keeping your code block clean.

iOS: Fluid and Interruptible Interactions

iPhone users expect animations to follow the movement of their fingers and be interruptible (being able to stop an animation midway with another tap).

  • Tip: Use spring curves (.spring, .interactiveSpring) widely on iOS via explicit animations. They provide that native, organic feel that characterizes the platform compared to traditional linear transitions.

6. Debugging and Animation Control

As your application in Xcode grows, you will inevitably encounter situations where something animates when it shouldn’t, or vice versa. Here are advanced techniques in Swift programming to tame SwiftUI.

Disabling Animations

Sometimes, a state change will trigger an implicit animation on a child component that you do not wish to animate. You can disable the animation on a specific branch of the hierarchy by injecting a nil animation.

Text("I will not animate")
    .scaleEffect(isScaled ? 1.5 : 1.0)
    .animation(nil, value: isScaled) // Disables animations for this view

Modifier Order Matters

The .animation modifier only applies to the modifiers that precede it in the code chain. This is a very common mistake when learning SwiftUI.

// THIS IS CORRECT
Text("Hello World")
    .opacity(isVisible ? 1.0 : 0.0) // This will animate
    .animation(.easeInOut, value: isVisible)
    .foregroundColor(isVisible ? .red : .blue) // This will NOT animate

// THIS IS ALSO CORRECT, BUT HAS A DIFFERENT RESULT
Text("Hello World")
    .opacity(isVisible ? 1.0 : 0.0) // This will animate
    .foregroundColor(isVisible ? .red : .blue) // This will ALSO animate
    .animation(.easeInOut, value: isVisible)

Always think of SwiftUI applying modifiers from top to bottom, wrapping the view in successive layers. The animation layer only wraps the changes declared above it.


7. Accessibility: Respecting the User

No article on being an iOS Developer would be complete without discussing accessibility. Many users suffer from motion sickness or discomfort with complex animations and enable the “Reduce Motion” option in their system settings.

In SwiftUI, you can detect this preference through the environment and adapt your animations (both implicit and explicit):

@Environment(\.accessibilityReduceMotion) var reduceMotion

var body: some View {
    Button("Touch me") {
        withAnimation(reduceMotion ? nil : .spring()) {
            isScaled.toggle()
        }
    }
    // Or if you use implicit animations:
    // .animation(reduceMotion ? nil : .spring(), value: isScaled)
}

If reduceMotion is true, we pass nil to the animation, making the state change instantaneous, thereby respecting the user’s accessibility choice.


8. Conclusion

The debate of Implicit vs Explicit Animation in SwiftUI isn’t about which is better, but which is the right tool for the job. As an iOS Developer, your fluency in Swift programming will depend on your ability to identify the context.

  • If you want a view to intelligently manage its own appearance based on the data it receives, use the .animation(_:value:) modifier implicitly.
  • If you are managing business logic, reacting to direct user intents (like button taps or gestures), and need the entire interface to react in unison in a controlled manner, wrap your state mutations in a withAnimation block to create an explicit animation.
Leave a Reply

Your email address will not be published. Required fields are marked *

Previous Article

Create Button with System Image in SwiftUI

Next Article

Custom Label Style in SwiftUI

Related Posts