3.6 导航:主干、侧枝与临时花


3.6 导航:主干、侧枝与临时花

本节摘要:单页长好了,多页要靠导航组织成园。UIKit 有三种栽法:UINavigationController 是主干(栈式推进,一层层往里钻)、UITabBarController 是侧枝(几个平行世界底部切换)、模态呈现是临时花(打断当前流程,办完就谢)。本节讲三者的结构语义与代码装配,演示阳台花园从单页长成"标签底座加导航主干"的三页结构,并给出"该用哪种"的判断准则。

从一棵树到一排树

到这里为止,阳台花园还只有一个详情页孤零零地立在窗口里。真实 App 的页面是成族的:列表、详情、添加、设置——它们之间的组织关系就是导航。UIKit 的三种容器各自对应一种用户心智:

  • 导航控制器(主干):像钻进一条走廊,推开一扇门看到更深一层,返回键退回上一层。栈结构:push 入栈、pop 出栈。
  • 标签控制器(侧枝):几个平行的世界,底部一排按钮切换,各自世界内部互不干扰。
  • 模态呈现(临时花):从当前页面上升起一层,逼用户先处理这件事(确认、登录、分享),处理完 dismiss 落下。

三种不是互斥的:成熟 App 常是"标签底座 + 每个标签里各一条导航主干 + 关键节点上开临时花"。App 设置页在第三个标签、点某株植物进详情是第一条主干上 push、点添加按钮弹出的表单是模态——一园三种栽法并存。

图 3-6 阳台花园的三层导航结构

图 3-6 阳台花园的三层导航结构

装配代码:从窗口根到模态

结构图看懂了,代码从外往里装配。第一步,把三种容器组装起来作为窗口根:

// 场景配置处(AppDelegate 或 SceneDelegate)搭建整园结构 func installRoot() { // 三个标签各自的根:前两个是导航主干,第三个是普通页面 let listVC = PlantListViewController() // 植物列表(第 4 章实现) let gardenNav = UINavigationController(rootViewController: listVC) gardenNav.tabBarItem = UITabBarItem(title: "花园", image: nil, tag: 0) let favVC = FavoriteViewController() let favNav = UINavigationController(rootViewController: favVC) favNav.tabBarItem = UITabBarItem(title: "收藏", image: nil, tag: 1) let settingsVC = SettingsViewController() settingsVC.tabBarItem = UITabBarItem(title: "设置", image: nil, tag: 2) let tabBar = UITabBarController() tabBar.viewControllers = [gardenNav, favNav, settingsVC] // 底座装上三条枝 window?.rootViewController = tabBar print("结构:Tab → 三个孩子,前两个是 Nav") }

第二步,主干上的推进与返回。push 会自动带上返回按钮与滑动返回手势,全都免费:

// 列表页里:点某株植物 → push 详情 func openDetail(for plant: Plant) { let detail = PlantDetailViewController(plant: plant) navigationController?.pushViewController(detail, animated: true) // navigationController 可选链:如果本页不在任何导航主干里,静默不动(3.2 的链式思想) } // 详情页里:一层层退的三个档位 @objc func backTapped() { if let nav = navigationController { nav.popViewController(animated: true) // 退一层 // nav.popToRootViewController(animated: true) // 一路退到栈底 // dismiss(animated: true) // 这是模态的退法,别混用 } } // 每次进出栈,3.3 的生命周期回调成对触发:push 详情时列表走 WillDisappear/DidDisappear

第三步,模态。给列表页加"添加植物"按钮,点击升起表单:

@objc func addTapped() { let form = AddPlantViewController() // 3.4 的表单页 let nav = UINavigationController(rootViewController: form)// 模态通常包一层导航,好放标题与取消 nav.modalPresentationStyle = .pageSheet // 页面单式:底下留缝,暗示"还有下层" present(nav, animated: true) } // 表单内部结束自己: @objc func cancelTapped() { dismiss(animated: true) } // 落下 @objc func saveTapped() { // 先存数据…… dismiss(animated: true) }

⚠️ 常见坑:push 与 present 混淆——push 要本页在导航主干上(navigationController 非 nil),present 任何页面都能用;对应地,退场用 pop 还是 dismiss 必须与进入方式配对,用错轻则没反应,重则把别人的页面关了。进来的路就是出去的路。

案例:流程归属判定练习

背景:产品接连提了四个需求,每条都要判断栽法。操作与判定:

需求 判定 理由
点植物看详情与养护记录 主干 push 层层深入,看完返回继续逛
顶部加"提醒设置"入口 标签侧枝 独立分区,与浏览流程无关
删除植物前弹确认 模态(警告式) 打断流程逼用户决策,办完即散
从详情"编辑植物信息" 主干 push 编辑是详情的延伸层次,保留返回路径

解读:判定词只有三个问题——这是更深的层次吗(push)、这是平行的分区吗(tab)、这件事必须现在办完吗(present)。三个都答否,大概率该做成页内展开的区块而不是新页面。变式:删除确认这种轻量打断也可用行动作表(下拉的选项面板)替代整页模态——打断成本更低;苹果的人机界面指南对"破坏性操作"建议优先用行动作表加红色确认项,整页模态留给需要输入的流程。

本节要点回顾

  • 三栽法语义:主干栈式深入、侧枝平行分区、临时花打断流程;
  • 装配自外向内:Tab 做根装 Nav,Nav 做根装页面,模态单独 present;
  • push/pop 配导航、present/dismiss 配模态,进出必须成对;
  • 导航栏返回键与滑动手势系统自带,包一层 Nav 的模态才有标题与取消位;
  • 判定三问:更深吗、平行吗、必须现在吗。

至此第 3 章收工:图纸、根系、节律、叶片、伸展、主干俱全。下一章让树繁茂起来——列表、网格、手势、动画。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U