Easing: Smoothing Functions in Mobile Animation

Author: IT Sectr Published: 2026-03-02 Reading time: 8 min
Easing (smoothing function) is a mathematical curve that determines the rate of change of an animation parameter over time. Instead of linear uniformly accelerated motion, easing simulates physical behavior: an object can accelerate (ease-in), decelerate (ease-out), or do both (ease-in-out). In iOS, easing is implemented through the CAMediaTimingFunction class (Core Animation) and SwiftUI modifiers (Animation.easeInOut). In Android — through PathInterpolator and TimeInterpolator. According to Apple Developer Documentation, a properly selected easing curve reduces perceived animation time by 20–35% without changing the actual duration. At IT Sectr, we standardized the ease-in-out curve (0.42, 0.0, 0.58, 1.0) as a corporate standard for all mobile projects.

Key Takeaways

  • Easing — an animation speed function that mimics physical movement: acceleration, deceleration, or a combination.
  • CAMediaTimingFunction — the iOS Core Animation class with 5 predefined curves and custom cubic Bézier.
  • SwiftUI Animation — modifiers .easeIn, .easeOut, .easeInOut, .spring and .timingCurve for declarative easing.
  • PathInterpolator — the Android equivalent of easing with a cubic Bézier curve through two control points.
  • Cubic Bézier — a universal format for specifying easing curves, common to iOS, Android, and the web.

What is Easing?

Easing (smoothing function) is a mathematical function f(t) that maps the linear time t ∈ [0,1] to a non-linear animation value. If an animation changes an object’s position from A to B in 500 ms, easing determines how the object moves between these points at each moment in time. Linear easing (f(t) = t) means constant speed — the object moves mechanically. Non-linear easing feels natural: the object accelerates (ease-in), decelerates (ease-out), or has a smooth start and end (ease-in-out).

Easing functions are described by cubic Bézier curves, where the X-axis is normalized time and the Y-axis is normalized progress. Two control points (P1, P2) define the curve shape. Standard curves are fixed by the CSS specification: ease (0.25, 0.1, 0.25, 1.0), ease-in (0.42, 0.0, 1.0, 1.0), ease-out (0.0, 0.0, 0.58, 1.0), ease-in-out (0.42, 0.0, 0.58, 1.0). These same values are used in CAMediaTimingFunction in iOS and PathInterpolator in Android. According to Material Design Motion Guidelines, easing curves should match the context: for appearance — ease-out (fast start), for disappearance — ease-in (fast finish).

Easing in iOS: CAMediaTimingFunction

In iOS, easing primarily works through the CAMediaTimingFunction class from Core Animation. It accepts a cubic Bézier curve as two control points (c1x, c1y, c2x, c2y) and applies to CAAnimation (CABasicAnimation, CAKeyframeAnimation). CAMediaTimingFunction supports 5 predefined functions: linear, easeIn, easeOut, easeInEaseOut, and default (equivalent to easeInEaseOut).

CAMediaTimingFunction works at the CALayer level — the animation runs in a separate render server process, guaranteeing 60fps even with complex transformations. For UIView property animation (frame, alpha, transform), UIKit uses UIView.animate with the options parameter: .curveEaseInOut, which internally also creates a CAMediaTimingFunction with the corresponding curve. In iOS 17+, UIViewAnimationCurve.custom with BlockParameters for custom Bézier was added — this eliminates the need to write custom CAAnimation. CAMediaTimingFunction is also used in UIKit Dynamics and UIScrollView for deceleration (UIScrollViewDecelerationRate).

Easing in SwiftUI

In SwiftUI, easing is set through the Animation structure with modifiers .easeIn, .easeOut, .easeInOut, .spring and .interpolatingSpring. Unlike UIKit, where animation is procedural (beginAnimations/commitAnimations), SwiftUI uses a declarative approach: animation is a state property that changes, and the framework itself determines how to animate the transition.

The .timingCurve(c0x, c0y, c1x, c1y) modifier accepts cubic Bézier control points directly — this is a full analog of CAMediaTimingFunction. SwiftUI Animation supports predefined curves as static properties: Animation.easeInOut is equivalent to ease-in-out (0.42, 0.0, 0.58, 1.0). For spring animation, Animation.spring(response:dampingFraction:blendDuration) is used, where dampingFraction < 1 creates a spring effect with damping. At WWDC 2023, Apple introduced Animation.keyframes — frame-by-frame animation with custom easing on each keyframe, giving full control over the trajectory.

Easing in Android: PathInterpolator

In Android, easing is implemented through PathInterpolator (API 21+) — a class that accepts a Path object with a cubic Bézier curve. Control points are specified via Path.cubicTo(x1, y1, x2, y2, x3, y3), where (x1,y1) is the first control point, (x2,y2) is the second, and (x3,y3) is the end point (always 1,1). PathInterpolator is required if a custom curve is needed; for standard scenarios, AccelerateDecelerateInterpolator (ease-in-out), AccelerateInterpolator (ease-in), and DecelerateInterpolator (ease-out) are available.

In Jetpack Compose, easing is set through the Easing object from the androidx.compose.animation package. FastOutSlowInEasing, LinearOutSlowInEasing, FastOutLinearInEasing are available, as well as custom Easing through CubicBezierEasing. Compose Easing is applied in animate*AsState, AnimatedVisibility and AnimatedContent. The key difference from the View system: Compose interpolates easing on the Kotlin side (not in RenderNode), which gives more flexibility for custom animations but requires optimization on 120Hz displays.

Code Examples

iOS: CAMediaTimingFunction with CABasicAnimation

Basic layer position animation with a custom ease-in-out curve. CAMediaTimingFunction accepts cubic Bézier control points that define a smooth start and end of movement.

swift
import UIKit

func animateLayerPosition() {
    let animation = CABasicAnimation(keyPath: "position")
    animation.fromValue = CGPoint(x: 0, y: 0)
    animation.toValue = CGPoint(x: 300, y: 400)
    animation.duration = 0.8

    let easing = CAMediaTimingFunction(
        controlPoints: 0.42, 0.0, 0.58, 1.0
    )
    animation.timingFunction = easing
    animation.fillMode = .forwards
    animation.isRemovedOnCompletion = false

    layer.add(animation, forKey: "positionAnimation")
}

The control points (0.42, 0.0, 0.58, 1.0) define the standard ease-in-out curve. SwiftUI uses the same points for Animation.easeInOut. For ease-in, use (0.42, 0.0, 1.0, 1.0); for ease-out — (0.0, 0.0, 0.58, 1.0). Core Animation executes the animation in the render server at 60/120 fps.

SwiftUI: Easing with timingCurve

Declarative animation in SwiftUI with a custom curve and spring effect. The animation modifier binds to the isAnimated change.

swift
import SwiftUI

struct EasingExampleView: View {
    @State private var isAnimated = false

    var body: some View {
        VStack {
            Circle()
                .fill(Color.blue)
                .frame(width: 60, height: 60)
                .offset(x: isAnimated ? 100 : 0, y: 0)
                .animation(
                    .timingCurve(0.42, 0.0, 0.58, 1.0, duration: 0.6),
                    value: isAnimated
                )

            Button("Toggle") {
                isAnimated.toggle()
            }
        }
    }
}

The .timingCurve(c0x, c0y, c1x, c1y, duration) modifier creates a custom easing animation in SwiftUI. The value parameter binds the animation to a specific state — when isAnimated changes, SwiftUI automatically applies the curve. For spring animation, use .spring(response: 0.4, dampingFraction: 0.6) instead of timingCurve.

Android: PathInterpolator with Custom Easing

The ease-in-out equivalent in Android through PathInterpolator. The control points (0.42, 0.0, 0.58, 1.0) define the same curve as in iOS.

kotlin
import android.animation.ObjectAnimator
import android.graphics.Path
import android.view.animation.PathInterpolator
import android.view.View

fun View.fadeInWithEase() {
    val path = Path().apply {
        cubicTo(0.42f, 0.0f, 0.58f, 1.0f, 1.0f, 1.0f)
    }
    val interpolator = PathInterpolator(path)

    ObjectAnimator.ofFloat(this, "alpha", 0f, 1f).apply {
        duration = 500
        interpolator = interpolator
        start()
    }
}

PathInterpolator with control points (0.42, 0.0, 0.58, 1.0) is identical to CAMediaTimingFunction ease-in-out on iOS. For cross-platform projects, use the same control points — this guarantees visually identical animation behavior. Material Design recommends FastOutSlowInInterpolator (0.4, 0.0, 0.2, 1.0) as an alternative for Android-specific interfaces.

Jetpack Compose: Easing with CubicBezierEasing

In Compose, easing is set through CubicBezierEasing with the same control points. The result is a smooth element appearance with transparency and offset.

kotlin
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.CubicBezierEasing
import androidx.compose.animation.core.tween
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.*

@Composable
fun EasingComposable() {
    var visible by remember { mutableStateOf(false) }
    val easing = CubicBezierEasing(0.42f, 0.0f, 0.58f, 1.0f)

    val alpha by animateFloatAsState(
        targetValue = if (visible) 1f else 0f,
        animationSpec = tween(durationMillis = 500, easing = easing)
    )

    Column {
        Button(onClick = { visible = !visible }) {
            Text("Toggle")
        }

        Box(
            modifier = Modifier
                .size(100.dp)
                .graphicsLayer(alpha = alpha)
                .background(MaterialTheme.colorScheme.primary)
        )
    }
}

CubicBezierEasing in Compose accepts the same 4 control point parameters. animateFloatAsState automatically interpolates the alpha value with the specified curve. For comparison: FastOutSlowInEasing in Compose is equivalent to (0.4, 0.0, 0.2, 1.0) — the standard Material Design 3 curve. Custom curves via CubicBezierEasing give full control for branded animations.

Frequently Asked Questions

How is easing different from interpolator?

Easing is a general concept of a smoothing function used in iOS (CAMediaTimingFunction), the web (CSS ease-in-out) and design. Interpolator is the implementation of this concept in Android (TimeInterpolator). Essentially they are the same thing — a mathematical curve of animation speed. The difference is only in the name and platform API. In cross-platform projects, the term easing is used as a universal one.

What easing functions are available in iOS?

iOS provides CAMediaTimingFunction with 5 predefined functions: linear, easeIn, easeOut, easeInEaseOut, and custom (cubic Bézier). In SwiftUI, Animation.easeIn, Animation.easeOut, Animation.easeInOut, Animation.spring and Animation.interpolatingSpring are available. For custom curves, use Animation.timingCurve(c0x, c0y, c1x, c1y, duration:).

Can I use the same easing function on iOS and Android?

Yes, if you set the same cubic Bézier curve. CAMediaTimingFunction in iOS and PathInterpolator in Android accept cubic Bézier control points (x1, y1, x2, y2). For example, the ease-in-out (0.42, 0.0, 0.58, 1.0) curve will give identical behavior on both platforms. Material Design uses this approach for cross-platform animations.

How to choose the right easing curve for my application?

Use Material Design Motion Guidelines: for element appearance — ease-out (fast start, smooth finish), for disappearance — ease-in (fast finish), for movement between screens — ease-in-out. Use spring animation for interactive elements (buttons, cards). Avoid linear — it looks unnatural.

Why does my easing animation look jerky?

Jank occurs when the animation does not reach 60fps. Check: are you using CAMediaTimingFunction (not a timer) in iOS, PathInterpolator (not Handler.post) in Android. Heavy computations on the main thread, complex layer hierarchy, or a large drawable can cause frame drops. In iOS, use Core Animation with shouldRasterize for complex layers; in Android — Hardware Acceleration and GPU rendering.

Summary

  • Easing is an animation speed function that makes movement natural through non-linear parameter changes over time.
  • iOS CAMediaTimingFunction and SwiftUI Animation.timingCurve implement easing through a cubic Bézier curve with two control points.
  • Android PathInterpolator and Compose CubicBezierEasing are full iOS analogs for custom smoothing curves.
  • Cross-platform easing curves are defined by the same control points — the basis for animation unification.
  • Material Design 3 recommends fast-out-slow-in for appearance, linear-out-slow-in for disappearance.
  • Spring animation in SwiftUI (Animation.spring) and Compose (spring()) creates a physical spring effect with dampingFraction.
  • Linear easing is not recommended for interface animations — it looks unnatural and mechanical.

We will develop a mobile application turnkey

IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.

Discuss the project

Read also