SwiftUI 入门教程 - NavigationStack
对于 iOS 开发者来说,导航视图无疑是最常用的组件之一。当 SwiftUI 首次发布时,官方提供了 NavigationView
视图,以供开发人员构建基于导航的用户界面。
随着 iOS16 的发布,NavigationView
已经被苹果官方弃用,并引入了一个名为 NavigationStack
的新视图来呈现视图堆栈。最重要的是,开发者可以利用这个新视图来构建数据驱动的导航。
NavigationView 的使用方式
在 iOS16 之前,我们可以使用 NavigationView 加 NavigationLink 来进行导航栏的显示以及导航跳转:
var body: some View {
NavigationView {
NavigationLink {
Text("详情页")
} label: {
Text("去详情")
}
}
}
上述代码创建了一个带有导航控制器的视图,并且带有《去详情》的一个按钮。点击该按钮可以导航到详情页面。
NavigationStack 的使用方式
使用 NavigationStack 在根视图上显示视图堆栈。用户可以通过点击 NavigationLink
将视图添加到视图栈的顶部,或者使用后退按钮或滑动手势来删除视图。
视图栈总是显示栈顶的视图,并且根视图是不能被删除的。
我们可以使用 navigationDestination(for:destination:)
修饰符将视图与相关联的数据进行绑定,然后初始化一个 NavigationLink 来进行导航跳转。
假设我们要实现这样一个需求:首页显示猫咪的列表,点击每一条会跳转到详情页。
首先,定义一个结构体用来存储猫咪的数据:
struct Cat: Identifiable, Hashable {
let name: String
let id: UUID
}
因为 List 对每一条数据要求唯一性,所以我们的结构体要遵守上述的两个协议。
接着,创建一个名为 CatDetailView
的视图来表示详情页,编写以下代码:
struct CatDetailView: View {
let cat: Cat
var body: some View {
Text(cat.name)
}
}
最后,就是创建一个名为 CatListView
的视图,用来表示列表页,编写以下代码:
struct CatListView: View {
let cats = [Cat(name: "red cat", id: UUID()),
Cat(name: "black cat", id: UUID()),
Cat(name: "orange cat", id: UUID())]
var body: some View {
NavigationStack {
List(cats) { cat in
NavigationLink(cat.name, value: cat)
}
.navigationDestination(for: Cat.self) { cat in
CatDetailView(cat: cat)
}
}
}
}
效果图如下:
多个 Navigation Destination 修饰符
开发者可以定义多个 navigationDestination
修饰符来处理不同类型的导航链接。在前面的例子中我们处理了类型为 Cat 的导航处理。假设我们需要在首页加一种类型为 Dog 的导航处理,可以直接在后面再加一个 navigationDestination
。比如下面的代码:
NavigationStack {
List(cats) { cat in
NavigationLink(cat.name, value: cat)
}
List(dogs) { dog in
NavigationLink(dog.name, value: dog)
}
.navigationDestination(for: Cat.self) { cat in
CatDetailView(cat: cat)
}
.navigationDestination(for: Dog.self) { dog in
DogDetailView(dog: dog)
}
}
Tips: DogDetailView 与 Dog 皆与猫咪的类似,仅改变一下名字。冗余代码就不在此贴出了。
导航栏的状态管理
与之前的 NavigationView 不同,新的 NavigationStack 可以方便地跟踪导航状态。NavigationStack 视图有另一个初始化方法,它接受一个path参数,该参数绑定到堆栈的导航状态:
public init(path: Binding<Data>, @ViewBuilder root: () -> Root)
示例代码如下:
@State private var presentedCats: [Cat] = []
var body: some View {
NavigationStack(path: $presentedCats) {
List(cats) { cat in
NavigationLink(cat.name, value: cat)
}
.navigationDestination(for: Cat.self) { cat in
VStack {
Text("\(presentedCats.count), \(presentedCats.description)")
CatDetailView(cat: cat)
}
}
}
}
首先声明了一个 State 的属性用来保存当前导航的状态,然后在 NavigationStack 的初始化方法中将其传递进去即可。