Transition:基础,移动应用中的过渡动画

作者: IT Sectr 发布日期: 2026-03-02 阅读时间: 11 分钟

Transition(过渡动画)——一种将一种内容可视地替换为另一种内容的机制:屏幕、片段、视图或界面状态。正确实现的过渡可以引导用户注意力,解释元素之间的相互关系,并使导航变得直观。根据 Material Design 指南(2026),具有精心设计过渡的应用在第二次会话中用户留存率提高 22%。有关其他动画类型的更多信息,请阅读动画通用指南

要点

  • Transition — 屏幕、片段或状态之间的内容更改动画。
  • UIView.transition — iOS (UIKit) 中过渡动画的主要 API。
  • FragmentTransaction.setCustomAnimations — Android 中 Fragment 之间过渡动画的配置。
  • Transition Framework (android.transition) — Android 的场景到场景声明式动画系统。
  • UIViewControllerAnimatedTransitioning — iOS 中控制器之间自定义过渡的协议。

什么是 Transition?

Transition — 是一种在一种视觉界面状态被另一种替换时发生的动画。与 Property Animation(单个对象属性的动画)不同,Transition 管理着一整套变化:元素的出现/消失、位置、大小和属性在单个过渡中的改变。Transition 解决了内容平滑变化的问题,使用户理解什么移动到了哪里。

在移动平台上,Transition 应用于三种主要场景:屏幕之间的导航(push、pop、modal present)、片段的切换(Android FragmentTransaction)和屏幕内部状态的变化(展开卡片、切换标签)。每种场景都需要自己的方法:导航过渡 — iOS 中的 UIViewControllerAnimatedTransitioning 或 Android 中的 ActivityOptions,屏幕内过渡 — Transition Framework 或 UIView.transition。

根据 Apple HIG(2026),标准过渡的持续时间不应超过 400–500 毫秒 — 更长的动画会被视为延迟。Material Design 建议移动过渡使用 300–350 毫秒。对于大元素(卡片、图像),时间可以增加到 500–600 毫秒而不会损失感知性能。

过渡动画的类型

过渡类型根据什么内容如何变化分为几个类别。基本类型:淡入淡出(cross-fade)— 一个逐渐消失,另一个出现;滑动(slide)— 一个屏幕离开,另一个进入;缩放(scale/zoom)— 元素放大,打开详细屏幕。组合过渡结合了多种效果。

过渡类型描述iOS APIAndroid API
Cross-fade淡入淡出:一个内容消失,另一个出现UIView.transition(with:duration:options:.transitionCrossDissolve)TransitionSet + Fade()
Slide滑动:内容水平或垂直离开/进入CATransition(type: .push, subtype: .fromLeft)Slide() / FragmentTransaction.setCustomAnimations
Scale / Zoom缩放:元素放大到全屏UIViewPropertyAnimator 配合 scale 动画Scale() + ActivityOptions.makeScaleUpAnimation
Explode爆炸式:内容在消失时散开CATransition(type: .reveal)Explode() (API 21+)
Fade-through通过白色/黑色屏幕淡入淡出UIView.transition(with: .transitionCrossDissolve,白色背景)通过 ChangeBounds 实现的 Fade()

Material Design 规则:在一个应用内使用一种过渡类型,以创建统一的视觉语言。建议使用 slide 进行同级屏幕之间的导航(master → detail),cross-fade 用于同一屏幕内的内容更改(标签、视图切换),scale/zoom 用于从卡片到详细屏幕的过渡(产品卡片 → 描述)。

iOS 中的 Transition (Swift)

在 iOS 中,UIView.transition 是在单个 View 内对内容更改进行动画的最简单方法。它支持 cross-dissolve、curl-up、curl-down、flip-from-left、flip-from-right。对于 UIViewController 之间的导航过渡,使用 UINavigationControllerDelegate 配合 UIViewControllerAnimatedTransitioning — 这提供了对 push/pop 动画的完全控制。

swift
import UIKit

// UIView.transition:使用 cross-dissolve 切换图像
UIView.transition(with: imageView,
    duration: 0.4,
    options: [.transitionCrossDissolve, .curveEaseInOut]
) {
    self.imageView.image = UIImage(named: "new_image")
} completion: { _ in
    print("Transition 完成")
}

// UIView.transition:切换两个 View
UIView.transition(from: firstView, to: secondView,
    duration: 0.5,
    options: [.transitionFlipFromRight, .showHideTransitionViews]
) { _ in
    // firstView 隐藏,secondView 显示
}

// 用于图层的 CATransition(无 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 — iOS 10+ 上自定义过渡的现代替代方案。与 UIView.transition 不同,PropertyAnimator 支持暂停、取消和 reverse(反向动画),这对于交互式过渡(滑动返回)非常重要。对于 UIViewController 之间的动画,请将 UIPercentDrivenInteractiveTransition 与 PropertyAnimator 结合使用 — 这是交互式导航的标准模式。

Android 中的 Transition (Kotlin)

在 Android 中,带有 setCustomAnimations 的 FragmentTransaction 是 Fragment 之间过渡动画的主要方法。参数:enter(新 Fragment 的进入动画)、exit(旧的退出动画)、popEnter 和 popExit — 用于后退导航。FragmentTransaction 支持资源动画 (R.anim) 和 Transition Framework (R.transition)。

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

// 带资源动画的 FragmentTransaction
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()

// 带 Transition Framework 的 FragmentTransaction
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:启动 Activity 时的动画
val options = ActivityOptions.makeScaleUpAnimation(
    view, 0, 0, view.width, view.height
).toBundle()
startActivity(intent, options)

Android 最佳实践:Slide() — 用于导航中 Fragment 的替换(enter = Slide(Gravity.END), exit = Slide(Gravity.START))。Fade() — 用于显示/隐藏覆盖层和对话框。Explode() — 用于强调过渡(从列表中删除元素)。对于旧设备 (API < 21),使用 R.anim 作为后备方案。Transition Framework 不支持 SurfaceView、TextureView、GLSurfaceView — 对于视频和摄像头,仅使用通过 R.anim 实现的动画。

iOS 中的自定义 UIViewController 过渡

UIViewControllerAnimatedTransitioning — iOS 中用于在控制器之间创建完全自定义过渡动画的协议。它提供对容器 View (transitionContext.containerView)、源 View 和目标 View 的访问。与 UIPercentDrivenInteractiveTransition 结合使用时,这种方法允许通过手指滑动创建交互式过渡。

swift
import UIKit

"> 自定义过渡动画器
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)
        }
    }
}

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

交互式过渡:向源屏幕添加 UIPanGestureRecognizer。当手势触发时,创建 UIPercentDrivenInteractiveTransition 并调用 update(CGFloat(percent))。要在未达到阈值(通常为 30%)时取消,请调用 cancel()。此模式默认用于 UINavigationController 中的 pop 手势以及具有 pagingEnabled 的 UIScrollView。对于自定义手势,实现 UIViewControllerInteractiveTransitioning。

Android 中的 Transition Framework:场景到场景

Transition Framework (android.transition) — 是一个声明式动画系统,自动在两个场景 (Scene) 之间的变化进行动画。场景是具有特定状态的 View 集合。在从 Scene A 到 Scene B 的过渡中,Transition Framework 分析差异并动画显示:位置变化 (ChangeBounds)、大小变化 (ChangeClipBounds, ChangeTransform)、出现 (Fade) 和消失 (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:ChangeBounds + Fade 组合
val transition = TransitionSet().apply {
    ordering = TransitionSet.ORDERING_TOGETHER
    addTransition(ChangeBounds())
    addTransition(Fade(Fade.IN))
    duration = 400L
    interpolator = FastOutSlowInInterpolator()
}

// 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
TransitionManager.go(scene2, AutoTransition().apply {
    duration = 350L
})

// TransitionManager.beginDelayedTransition — 无场景
TransitionManager.beginDelayedTransition(sceneRoot, ChangeBounds())
itemView.layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT
itemView.requestLayout() "> layout 的变化自动动画化

ChangeBounds — Android 中最常用的过渡,对 View 的位置和大小变化进行动画。ChangeTransform 对 rotation 和 scale 进行动画。ChangeImageTransform — 用于 ImageView 的动画 (centerCrop → fitCenter)。对于层次结构变化的复杂过渡,使用带有 ordering = ORDERING_SEQUENTIAL 的 TransitionSet(动画按顺序执行,而不是并行)。根据 Android Developers 文档(2026),Transition Framework 支持所有标准 View,但不支持表面动画(SurfaceView、TextureView、VideoView)。

常见问题

Transition 和 Animation 有什么区别?

Animation — 单个对象属性的动画(alpha、translation、scale)。Transition — 对象或状态集合变化的动画(片段 → 片段,场景 → 场景)。Transition 同时管理多个变化:出现、消失、移动、大小变化 — 并自动对两个状态之间的差异进行动画。Animation 需要手动管理每个属性。

如何在 Android 片段之间实现平滑过渡?

使用带有 Transition Framework 的 FragmentTransaction.setCustomAnimations:fragment1.exitTransition = Fade(Fade.OUT), fragment2.enterTransition = Slide(Gravity.END)。对于序列:setReorderingAllowed(true) — 片段将被重新排序以实现平滑的并行过渡。对于简单情况,来自 R.anim 的 slide_in_right / slide_out_left 就足够了。对于后退导航,指定 popEnter 和 popExit。

Transition Framework 不对 View 的高度变化进行动画 — 怎么办?

Transition Framework 只对不会导致布局重新计算的属性进行动画。对于高度动画,使用 ChangeBounds — 它会自动对两个布局状态之间的 View 边界变化进行动画。在更改 LayoutParams 之前调用 TransitionManager.beginDelayedTransition(viewGroup, ChangeBounds())。如果高度通过 WRAP_CONTENT 变化,请确保前后的 layout_height 不同 — 如果两个状态都是 match_parent,ChangeBounds 将不起作用。

如何在 SwiftUI 中创建自定义过渡?

使用带有 AnyTransition 的 .transition() 修饰符:.transition(.move(edge: .trailing).combined(with: .opacity))。对于自定义过渡,实现 AnyTransition.asymmetric(insertion: .move(edge: .trailing), removal: .move(edge: .leading))。对于 NavigationStack 内的动画,在 iOS 18+ 中使用 .navigationTransition(.slide)。SwiftUI 不支持 UIViewControllerAnimatedTransitioning — 仅支持通过 AnyTransition 的声明式过渡。

如何测量 Transition 的性能?

使用 Profile GPU Rendering(Android)或 Core Animation Instrument(iOS)。对于 Android,通过 adb shell setprop debug.transition.profiling 1 添加 Transition 分析 — 日志中将显示过渡每个阶段的持续时间。在 iOS 上,在模拟器中启用 Slow Animations。目标指标:持续时间 < 400 毫秒,FPS >= 55,frame drops — 每次过渡不超过 2 次。对于具有 15+ 个同时动画 View 的复杂场景,使用 duration 300 毫秒的 ChangeBounds。

总结

  • Transition — 屏幕、片段或界面状态之间的内容更改动画。
  • UIView.transition (iOS) — View 内 cross-dissolve、flip、curl 过渡的基础 API。
  • UIViewControllerAnimatedTransitioning (iOS) — 控制器之间自定义过渡的协议。
  • FragmentTransaction.setCustomAnimations (Android) — Fragment 之间的过渡动画。
  • Transition Framework (android.transition) — 声明式场景系统:ChangeBounds、Fade、Slide、Explode。
  • 建议持续时间:移动端 300–400 毫秒,大元素可达 600 毫秒。
  • 每个应用使用一种过渡类型以实现统一的视觉语言(Material Design)。

我们将开发一款交钥匙移动应用程序

IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。

讨论项目

另请阅读