Transition: basics, transition animations in mobile apps

Author: IT Sectr Published: 2026-03-02 Reading time: 11 min

Transition is a visual change mechanism that replaces one content with another: screens, fragments, views, or interface states. A properly implemented transition directs the user's attention, explains the relationship between elements, and makes navigation intuitive. According to Material Design Guidelines (2026), apps with well-designed transitions show 22% higher user retention on the second session. Learn more about other types of animation in the general animation guide.

Key Takeaways

  • Transition — animation of content change between screens, fragments or states.
  • UIView.transition — the main API for transition animations in iOS (UIKit).
  • FragmentTransaction.setCustomAnimations — configuring transition animations between Fragments in Android.
  • Transition Framework (android.transition) — Android's declarative animation system for scene-to-scene transitions.
  • UIViewControllerAnimatedTransitioning — iOS protocol for custom view controller transitions.

What is Transition?

Transition is an animation that occurs when replacing one visual interface state with another. Unlike Property Animation (animating a single object's property), Transition manages a whole set of changes: appearance/disappearance of elements, changing their position, size and properties within a single transition. Transition solves the problem of smooth content change so that the user understands what moved where.

On mobile platforms, Transition is used in three main scenarios: navigation between screens (push, pop, modal present), fragment changes (Android FragmentTransaction) and in-screen state changes (card expansion, tab switching). Each scenario requires its own approach: navigation transitions — UIViewControllerAnimatedTransitioning in iOS or ActivityOptions in Android, in-screen transitions — Transition Framework or UIView.transition.

According to Apple HIG (2026), the duration of a standard transition should not exceed 400–500 ms — longer animations are perceived as lag. Material Design recommends 300–350 ms for mobile transitions. For large elements (cards, images), the time can increase to 500–600 ms without perceived performance loss.

Types of Transition Animations

Transition types are divided into several categories depending on which content changes and how. Basic types: cross-fade — smooth disappearance of one and appearance of another; slide — one screen moves out, another moves in; scale/zoom — an element enlarges, opening a detail screen. Combined transitions mix several effects.

Transition TypeDescriptioniOS APIAndroid API
Cross-fadeFade: one content disappears, another appearsUIView.transition(with:duration:options:.transitionCrossDissolve)TransitionSet + Fade()
SlideSlide: content moves in/out horizontally or verticallyCATransition(type: .push, subtype: .fromLeft)Slide() / FragmentTransaction.setCustomAnimations
Scale / ZoomScaling: element enlarges to full screenUIViewPropertyAnimator with scale animationScale() + ActivityOptions.makeScaleUpAnimation
ExplodeExplosion: content splits apart when disappearingCATransition(type: .reveal)Explode() (API 21+)
Fade-throughFade through white/black screenUIView.transition(with: .transitionCrossDissolve, with white background)Fade() via ChangeBounds

Material Design rules: use one transition type within a single app to create a unified visual language. Recommended: slide for navigation between same-level screens (master → detail), cross-fade for content change within one screen (tabs, view switching), scale/zoom for transitioning from a card to a detail screen (product card → description).

Transitions in iOS (Swift)

On iOS, UIView.transition is the simplest way to animate content changes within a single View. It supports cross-dissolve, curl-up, curl-down, flip-from-left, flip-from-right. For navigation transitions between UIViewController, use UINavigationControllerDelegate with UIViewControllerAnimatedTransitioning — this gives full control over push/pop animation.

swift
import UIKit

// UIView.transition: image change with cross-dissolve
UIView.transition(with: imageView,
    duration: 0.4,
    options: [.transitionCrossDissolve, .curveEaseInOut]
) {
    self.imageView.image = UIImage(named: "new_image")
} completion: { _ in
    print("Transition completed")
}

// UIView.transition: swapping two views
UIView.transition(from: firstView, to: secondView,
    duration: 0.5,
    options: [.transitionFlipFromRight, .showHideTransitionViews]
) { _ in
    // firstView hidden, secondView shown
}

// CATransition for layer (without Auto Layout)
let transition = CATransition()
transition.type = .push
transition.subtype = .fromRight
transition.duration = 0.35
transition.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
view.layer.add(transition, forKey: "pushTransition")

UIViewPropertyAnimator is a modern alternative for custom transitions on iOS 10+. Unlike UIView.transition, PropertyAnimator supports pause, cancellation and reverse animation, which is important for interactive transitions (swipe-to-go-back). For animation between UIViewControllers, use UIPercentDrivenInteractiveTransition together with PropertyAnimator — this is the standard pattern for interactive navigation.

Transitions in Android (Kotlin)

On Android, FragmentTransaction with setCustomAnimations is the main way to animate transitions between Fragments. Parameters: enter (animation for the new Fragment entering), exit (animation for the old Fragment exiting), popEnter and popExit — for back navigation. FragmentTransaction supports animation resources (R.anim) and Transition Framework (R.transition).

kotlin
import androidx.fragment.app.FragmentTransaction
import androidx.transition.TransitionInflater

// FragmentTransaction with resource animations
supportFragmentManager.beginTransaction()
    .setCustomAnimations(
        R.anim.slide_in_right,  // enter
        R.anim.slide_out_left,  // exit
        R.anim.slide_in_left,   // popEnter
        R.anim.slide_out_right  // popExit
    )
    .replace(R.id.container, DetailFragment())
    .addToBackStack(null)
    .commit()

// FragmentTransaction with Transition Framework
val detailFragment = DetailFragment()
detailFragment.enterTransition = TransitionInflater.from(this)
    .inflateTransition(R.transition.slide_right)
detailFragment.exitTransition = TransitionInflater.from(this)
    .inflateTransition(R.transition.fade_out)

supportFragmentManager.beginTransaction()
    .replace(R.id.container, detailFragment)
    .addToBackStack(null)
    .commit()

// ActivityOptions: animation when starting an Activity
val options = ActivityOptions.makeScaleUpAnimation(
    view, 0, 0, view.width, view.height
).toBundle()
startActivity(intent, options)

Android best practices: Slide() — for Fragment replacement in navigation (enter = Slide(Gravity.END), exit = Slide(Gravity.START)). Fade() — for showing/hiding overlays and dialogs. Explode() — for accent transitions (deleting an item from a list). For older devices (API < 21) use R.anim resources as fallback. Transition Framework does not support SurfaceView, TextureView, GLSurfaceView — for video and camera use only R.anim animations.

Custom UIViewController Transitions in iOS

UIViewControllerAnimatedTransitioning is an iOS protocol for creating fully custom view controller transition animations. It provides access to the container view (transitionContext.containerView), the source and target views. Combined with UIPercentDrivenInteractiveTransition, this approach allows creating interactive transitions with finger scrolling.

swift
import UIKit

"> Custom transition animator
class ScaleTransitionAnimator: NSObject, UIViewControllerAnimatedTransitioning {
    let isPresenting: Bool

    init(presenting: Bool) { self.isPresenting = presenting }

    func transitionDuration(using ctx: UIViewControllerContextTransitioning?) -> TimeInterval {
        return 0.4
    }

    func animateTransition(using ctx: UIViewControllerContextTransitioning) {
        guard let toView = ctx.view(forKey: .to),
              let fromView = ctx.view(forKey: .from)
        else { return }

        let container = ctx.containerView
        let initialScale: CGAffineTransform = CGAffineTransform(scaleX: 0.8, y: 0.8)
        let finalFrame = ctx.finalFrame(for: toView)

        if isPresenting {
            toView.transform = initialScale
            toView.alpha = 0
            toView.frame = finalFrame
            container.addSubview(toView)
        }

        UIView.animate(withDuration: transitionDuration(using: ctx),
                       delay: 0,
                       usingSpringWithDamping: 0.7,
                       initialSpringVelocity: 0.3) {
            if self.isPresenting {
                toView.transform = .identity
                toView.alpha = 1
                fromView.transform = CGAffineTransform(scaleX: 0.9, y: 0.9)
                fromView.alpha = 0
            } else {
                fromView.transform = initialScale
                fromView.alpha = 0
            }
        } completion: { _ in
            fromView.transform = .identity
            ctx.completeTransition(!ctx.transitionWasCancelled)
        }
    }
}

// Application in UINavigationControllerDelegate
extension MainViewController: UINavigationControllerDelegate {
    func navigationController(
        _ nc: UINavigationController,
        animationControllerFor operation: UINavigationController.Operation,
        from: UIViewController, to: UIViewController
    ) -> UIViewControllerAnimatedTransitioning? {
        return ScaleTransitionAnimator(presenting: operation == .push)
    }
}

Interactive transitions: add a UIPanGestureRecognizer to the source screen. When the gesture triggers, create a UIPercentDrivenInteractiveTransition and call update(CGFloat(percent)). To cancel when the threshold is not reached (usually 30%), call cancel(). This pattern is used by default in UINavigationController for pop gestures and in UIScrollView with pagingEnabled. For custom gestures, implement UIViewControllerInteractiveTransitioning.

Transition Framework in Android: Scene to Scene

Transition Framework (android.transition) is a declarative animation system that automatically animates changes between two scenes (Scene). A scene is a set of Views with a specific state. When transitioning from Scene A to Scene B, Transition Framework analyzes the difference and animates: position change (ChangeBounds), size change (ChangeClipBounds, ChangeTransform), appearance (Fade) and disappearance (Fade).

kotlin
import android.transition.Scene
import android.transition.TransitionManager
import android.transition.TransitionSet
import android.transition.AutoTransition
import android.transition.ChangeBounds
import android.transition.Fade

// TransitionSet: combination of ChangeBounds + Fade
val transition = TransitionSet().apply {
    ordering = TransitionSet.ORDERING_TOGETHER
    addTransition(ChangeBounds())
    addTransition(Fade(Fade.IN))
    duration = 400L
    interpolator = FastOutSlowInInterpolator()
}

// Transition between scenes in a ViewGroup
val sceneRoot = findViewById<ViewGroup>(R.id.scene_root)
val scene1 = Scene.getSceneForLayout(sceneRoot, R.layout.scene_initial)
val scene2 = Scene.getSceneForLayout(sceneRoot, R.layout.scene_expanded)

"> AutoTransition without manual configuration
TransitionManager.go(scene2, AutoTransition().apply {
    duration = 350L
})

// TransitionManager.beginDelayedTransition — without scenes
TransitionManager.beginDelayedTransition(sceneRoot, ChangeBounds())
itemView.layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT
itemView.requestLayout() "> Layout changes are animated automatically

ChangeBounds is the most used transition in Android, it animates View position and size changes. ChangeTransform animates rotation and scale. ChangeImageTransform — for ImageView animation (centerCrop → fitCenter). For complex transitions with hierarchy structure changes, use TransitionSet with ordering = ORDERING_SEQUENTIAL (animations execute in sequence, not in parallel). According to Android Developers (2026) documentation, Transition Framework supports all standard Views but does not support surface animation (SurfaceView, TextureView, VideoView).

Frequently Asked Questions

What is the difference between Transition and Animation?

Animation — animating properties of a single object (alpha, translation, scale). Transition — animating a change of a set of objects or states (fragment → fragment, scene → scene). Transition manages multiple changes simultaneously: appearance, disappearance, movement, size change — and automatically animates the difference between two states. Animation requires manual management of each property.

How to create a smooth transition between Android fragments?

Use FragmentTransaction.setCustomAnimations with Transition Framework: fragment1.exitTransition = Fade(Fade.OUT), fragment2.enterTransition = Slide(Gravity.END). For sequencing: setReorderingAllowed(true) — fragments will be reordered for smooth parallel transition. For simple cases, slide_in_right / slide_out_left from R.anim is enough. For back navigation, specify popEnter and popExit.

Transition Framework doesn't animate View height changes — what to do?

Transition Framework only animates properties that don't trigger layout recalculation. For height animation, use ChangeBounds — it automatically animates View boundary changes between two layout states. Call TransitionManager.beginDelayedTransition(viewGroup, ChangeBounds()) before changing LayoutParams. If height changes via WRAP_CONTENT, ensure layout_height differs before and after — ChangeBounds won't work if both states are match_parent.

How to create a custom transition in SwiftUI?

Use the .transition() modifier with AnyTransition: .transition(.move(edge: .trailing).combined(with: .opacity)). For custom transitions, implement AnyTransition.asymmetric(insertion: .move(edge: .trailing), removal: .move(edge: .leading)). For animation inside NavigationStack, use .navigationTransition(.slide) on iOS 18+. SwiftUI does not support UIViewControllerAnimatedTransitioning — only declarative transitions via AnyTransition.

How to measure Transition performance?

Use Profile GPU Rendering (Android) or Core Animation Instrument (iOS). For Android, enable Transition Profiling via adb shell setprop debug.transition.profiling 1 — logs will show the duration of each transition stage. On iOS, enable Slow Animations in the simulator. Target metrics: duration < 400 ms, FPS >= 55, frame drops — no more than 2 per transition. For complex scenes with 15+ simultaneously animated Views, use ChangeBounds with 300 ms duration.

Summary

  • Transition — animation of content change between screens, fragments or interface states.
  • UIView.transition (iOS) — basic API for cross-dissolve, flip, curl transitions within a View.
  • UIViewControllerAnimatedTransitioning (iOS) — protocol for custom view controller transitions.
  • FragmentTransaction.setCustomAnimations (Android) — transition animation between Fragments.
  • Transition Framework (android.transition) — declarative scene system: ChangeBounds, Fade, Slide, Explode.
  • Recommended duration: 300–400 ms for mobile, up to 600 ms for large elements.
  • One transition type per app for a unified visual language (Material Design).

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