Push Presentation — 移动应用中的导航模式,每个新屏幕都会添加到导航栈中,位于前一个屏幕之上。用户可以通过内容依次向前导航,并通过系统按钮或滑动手势返回。根据 Apple Developer, 2025,UINavigationController 在 85% 的 iOS 应用中被用作主要导航模式。在 Android 中,类似功能通过 FragmentManager 和 Navigation Component 中的 NavController 实现。
要点
Push Presentation — 是一种导航机制,其中每个新屏幕都被放置在栈顶,而前一个屏幕保留在它下面的内存中。用户通过依次打开详细屏幕深入内容,返回按钮将其带回上一步。
Push 导航的架构基于 LIFO(后进先出)数据结构。新屏幕总是添加到栈的末尾,删除只从栈顶进行。这保证了可预测的行为:用户始终知道返回按钮会将其精确地后退一步。导航栈 根据应用的复杂程度可以包含 2 到 10+ 个屏幕。
Push Presentation 是具有层次结构内容的应用中的主要模式:新闻推送、产品目录、多级菜单和文档。根据 Material Design,栈导航适合用户从一般到具体探索内容的场景,每个后续屏幕都能加深对主题的理解。
导航栈 — 是一个有序的屏幕集合,每个元素存储其 ViewController 或 Fragment 的状态。在 Push 操作期间,新屏幕被添加到栈中,其显示被动画化(iOS 中从右侧滑入,Android 中从底部滑入或淡入)并变为活动状态。
在 Pop 操作(按返回键)期间,顶部屏幕从栈中移除、销毁或进入内存,前一个屏幕变为活动状态。iOS 默认销毁被弹出的 ViewController,释放内存。Android 可以将 Fragment 保留在 back stack 中,无需重新创建即可恢复。
栈的深度影响性能:栈中的每个屏幕都消耗内存。建议 不要在栈中存储超过 10 个屏幕。对于深度导航,使用 PopToRoot 或使用新的根屏幕重新启动栈。Android 中的 Navigation Component 通过 SavedStateHandle 自动管理栈的状态。
UINavigationController — iOS 的容器控制器,管理 UIViewController 栈。它自动显示导航栏(navigation bar),包含当前屏幕的标题和返回按钮。默认的 Push 动画 — 从右向左滑入,为用户创造沉浸于内容的感觉。
屏幕添加通过 pushViewController(_:animated:) 完成。移除 — 通过 popViewController(animated:)。返回根屏幕使用 popToRootViewController(animated:)。UINavigationController 还通过 viewControllers 属性 — 栈中所有控制器的数组 — 支持程序化栈管理。
导航栏包含屏幕标题(title)、返回按钮和可选的操作按钮。从 iOS 11 开始,Large Titles(prefersLargeTitles)允许以放大的字体显示标题,该字体在滚动时会动画收缩。这改善了导航层次结构,并告知用户当前所在的部分。
let detailVC = DetailViewController()
detailVC.title = "Item Details"
navigationController?.pushViewController(detailVC, animated: true)
// 返回上一个屏幕
navigationController?.popViewController(animated: true)
// 返回根屏幕
navigationController?.popToRootViewController(animated: true)
Navigation Component — 用于声明式导航的 Android Jetpack 库。它提供 NavController,通过导航图(nav_graph)管理片段或 Compose 屏幕。Android 中的 Push 与 iOS 类似:每个新 Fragment 都被添加到 back stack,返回按钮恢复前一个。
NavHost — 显示来自 NavGraph 的当前目标的容器。NavGraph — 描述应用所有屏幕及它们之间连接的 XML 文件。转换通过 actions 定义,指定 destination 和可选参数。Navigation Component 自动处理系统返回按钮、动画和状态保存。
为了在屏幕之间传输数据,Navigation Component 支持 Safe Args — type-safe 参数类的代码生成。开发人员无需手动将数据放入 Bundle,而是在 NavGraph 中声明参数,并通过生成的 Directions 和 Args 类接收它们。Safe Args 消除了类型不匹配错误并简化了重构。
// NavGraph 在 XML 中的定义
<!-- res/navigation/nav_graph.xml -->
@navigation {
NavHost(startDestination = "list") {
composable("list") { ListScreen() }
composable(
"detail/{itemId}",
arguments = listOf(navArgument("itemId") { type = NavType.IntType })
) { DetailScreen(it.arguments()?.getInt("itemId") ?: 0) }
}
}
// 程序化导航
navController.navigate("detail/42")
Bottom Navigation 和 Push 导航的结合 — 移动应用中常见的模式。Bottom Navigation 的每个选项卡都有自己的屏幕栈。Navigation Component 通过 NavHost per tab 或使用单个 NavHost 为每个选项卡设置单独的图来支持这一点。切换选项卡不会重置 Push 栈的状态。
Push Presentation 和 Modal Presentation 解决不同的导航任务。Push 用于顺序查看内容,其中每个新屏幕深化上下文。Modal — 用于需要完成和专注的任务。在实践中,为特定场景选择正确的模式很重要。
主要选择标准:如果用户应该能够自由返回而不丢失上下文 — 选择 Push。如果任务是临时的并且阻塞主内容(表单、身份验证)— 使用 Modal。在同一个屏幕上混合模式(Modal 内的 Push)是允许的,但需要清晰的视觉层次。
对于逻辑上是主流程一部分的屏幕序列使用 Modal 是错误的。例如,订单创建向导(步骤 1 → 步骤 2 → 步骤 3)最好通过 Modal 容器内的 Push 实现,而不是通过模态窗口链。这保持了导航层次结构和返回的可预测性。
让我们看看两个平台上 Push 导航的完整实现。Swift 示例演示了 UINavigationController 与程序化栈管理和深度链接处理。Kotlin 示例展示了带有 NavHost、参数和动画的 Navigation Component。
class ListViewController: UIViewController {
func showDetail(_ itemId: Int) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let detailVC = storyboard.instantiateViewController(
withIdentifier: "DetailViewController"
) as! DetailViewController
detailVC.itemId = itemId
navigationController?.pushViewController(detailVC, animated: true)
}
}
class DetailViewController: UIViewController {
var itemId: Int = 0
override func viewDidLoad() {
super.viewDidLoad()
title = "Item #\(itemId)"
}
}
@Composable
fun PushNavigationApp() {
val navController = rememberNavController()
NavHost(navController = navController, startDestination = "list") {
composable("list") {
ListScreen(
onItemClick = { id -> navController.navigate("detail/$id") }
)
}
composable(
"detail/{itemId}",
arguments = listOf(navArgument("itemId") { type = NavType.IntType })
) { backStackEntry ->
val itemId = backStackEntry.arguments()?.getInt("itemId") ?: 0
DetailScreen(itemId = itemId, onBack = { navController.popBackStack() })
}
}
}
这些示例展示了基本的 Push 导航:列表 → 详情。iOS 使用 storyboard 和 UINavigationController,手动传递数据。Android 使用带有 type-safe 参数和自动 back stack 管理的 NavHost。两种方法都支持深度链接、自定义动画和旋转时的状态保存。
常见问题
Push 将屏幕添加到带有返回按钮的 UINavigationController 栈中。Present 打开一个没有返回按钮的模态窗口 — 用户必须显式关闭它。Push 适合顺序内容,Present 适合专注任务。在 SwiftUI 中,Push 对应 NavigationLink,Present 对应 .sheet。
Navigation Component 提供 popBackStack 到指定点的方法和 popUpTo 用于清理栈。为了防止栈溢出,请在 navigate 之前使用 popUpTo(startDestination) { inclusive = true }。这保证了栈同时包含不超过 5–7 个屏幕。
是的,Push 和 Modal 的组合是一种标准模式。例如:列表 → Push 到详情 → Modal 用于身份验证。建议 不要将 Push 放在 Modal 内部:模态窗口不应包含栈导航。如果模态窗口内部需要屏幕序列 — 请使用模态容器内的 Push。
Deep Links 在 Push 导航中在特定屏幕上打开应用。iOS 使用 URL 方案和带有 UINavigationController 的 Universal Links。Android 使用带有 NavDeepLink 的 Intent Filters。在两种情况下,系统解析链接并创建到目标屏幕的导航栈,同时保留返回的能力。
iOS 使用标准的滑入动画,可通过 UINavigationControllerDelegate 自定义。Android Navigation Component 通过 XML 资源(slide_in_right, slide_out_left)支持自定义动画。对于 Compose,请使用 AnimatedNavHost 配合 animateItemPlacement 修饰符。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。