一、前言
- 在 UIKit 的框架中,我们时常使用 UINavigationViewController 来管理页面的 push 和 pop,这是页面管理的基本操作。而到了 SwiftUI,该操作是交由 NavigationView 和 NavigationLink 来完成。
- 本文先从 NavigationView 的基本应用开始,再补充如何灵活的使用 NavigationView 来完成很多更细节化的需求。
二、基本概念
- 如下所示,用一个 demo 展示了 NavigationView 和 NavigationLink 的基本应用:
import SwiftUI
@main
struct iOS_testApp: App {
var body: some Scene {
WindowGroup {
NavigationView {
NavigationLink(
destination: Text("Destination"),
label: {
Text("Navigate")
})
}
}
}
}
- 在该示例中,提供了一个顶层 View,即 NavigationView,在 SwiftUI 中,NavigationView 相当于 UIKit 的 UINavigationViewController,它提供了整个页面导航环境的顶层容器,包裹在 NavigationView 下面的是 NavigationLink,它定义了本页面的视图以及待 push 的视图(通过点击)。
- 如在示例中,Text(“Navigate”)就是本页面的视图,而Text(“Destination”)就是点击跳转后的视图。主界面如下所示,点击 Navigate 即可 push:

- 点击 Navigate 后 push 新界面 Destination:

三、设置标题栏
- 在 NavigationView 的默认展示设置中,根级界面是没有标题栏的,而待 push 的界面默认带标题返回栏,但是标题为空。通过 .navigationBarTitle 修饰属性可以对标题进行设置:
import SwiftUI
@main
struct iOS_testApp: App {
var body: some Scene {
WindowGroup {
NavigationView {
NavigationLink(
destination: Text("Destination"),
label: {
Text("Navigate")
})
.navigationBarTitle("Main", displayMode: .large)
}
}
}
}
- 带 large 标题栏的 Navigate 界面,如下所示:

- 其中 displayMode 是一个枚举类型参数,支持 inline,large 和 automatic,分别表示小标题栏,大标题栏和自动选择,如果你选择 automatic,则一般系统会选择 large。
四、隐藏标题栏
- 某些情况下,如果不希望使用标题栏,或者不喜欢 NavigationView 提供的标题栏样式,对它提供的定制灵活性并不满意,而希望完全由自己接管和实现标题栏,在这种情况下,可以选择隐藏标题栏,隐藏标题栏通过 .navigationBarHidden(true) 来完成:
import SwiftUI
@main
struct iOS_testApp: App {
var body: some Scene {
WindowGroup {
NavigationView {
NavigationLink(
destination: Text("Destination")
.navigationBarHidden(true),
label: {
Text("Navigate")
})
.navigationBarTitle("Main", displayMode: .automatic)
}
}
}
}
- 隐藏了标题栏的 Destination 界面,如下所示:

五、编程实现页面返回逻辑
- 当隐藏了二级界面的标题栏后,我们岂不是把标题栏的返回按钮也隐藏了,那么要实现自己的返回按钮时,该怎么做呢?这时候就需要用到 SwiftUI 独有的机制:视图环境 @Environment,Environment 提供了视图共享的属性绑定服务,通过这些属性可以完成视图的基本操作,其中一个属性叫 presentationMode,该属性绑定了导航页面间的上下文关系,通过它的 dismiss 方法可以手动返回页面:
import SwiftUI
struct DestinationView: View {
@Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
var body: some View {
Text("Destination View")
.navigationBarHidden(true)
.onTapGesture {
self.presentationMode.wrappedValue.dismiss()
}
}