本篇目标
掌握 List 与 ForEach 的使用,理解 NavigationStack 导航结构,学会页面跳转、传值、导航栏标题与工具栏配置,能构建带层级跳转的界面。
一、List 基础
List 用于展示可滚动的行列表,是 iOS 中最常见的界面结构。
List {
Text("第一行")
Text("第二行")
Text("第三行")
}带分区:
List {
Section("分组一") {
Text("A")
Text("B")
}
Section("分组二") {
Text("C")
Text("D")
}
}设置样式:
List {
Text("第一行")
Text("第二行")
}
.listStyle(.insetGrouped)常用列表样式:
样式 | 说明 |
|---|---|
| 普通列表 |
| 分组列表 |
| 内嵌分组(默认) |
| 侧边栏风格 |
二、ForEach
ForEach 用于根据集合生成视图。
let fruits = ["苹果", "香蕉", "橘子"]
List {
ForEach(fruits, id: \.self) { fruit in
Text(fruit)
}
}说明:
id: \.self用于标识每个元素。集合元素需要可哈希。
可以遍历数组、区间、字典。
遍历区间:
List {
ForEach(1...5, id: \.self) { number in
Text("第 \(number) 行")
}
}遍历对象数组:
struct Item: Identifiable {
let id = UUID()
let name: String
}
let items = [
Item(name: "项目一"),
Item(name: "项目二"),
Item(name: "项目三")
]
List(items) { item in
Text(item.name)
}说明:
遵循
Identifiable协议后,不需要写id。推荐用
Identifiable,更简洁。
带索引:
List {
ForEach(Array(fruits.enumerated()), id: \.offset) { index, fruit in
Text("\(index + 1). \(fruit)")
}
}三、列表行交互
添加点击:
List {
ForEach(items) { item in
Button(item.name) {
print("点击了 \(item.name)")
}
}
}使用 NavigationLink:
List {
ForEach(items) { item in
NavigationLink(item.name) {
DetailView(item: item)
}
}
}删除与移动:
@State private var items = ["A", "B", "C"]
List {
ForEach(items, id: \.self) { item in
Text(item)
}
.onDelete { indexSet in
items.remove(atOffsets: indexSet)
}
.onMove { from, to in
items.move(fromOffsets: from, toOffset: to)
}
}启用编辑模式:
@State private var editMode: EditMode = .inactive
List {
// ...
}
.environment(\.editMode, $editMode)
Button("编辑") {
editMode = editMode == .active ? .inactive : .active
}四、NavigationStack
NavigationStack 是 iOS 16 引入的新导航容器,替代 NavigationView。
基础结构:
NavigationStack {
List {
NavigationLink("详情页") {
DetailView()
}
}
.navigationTitle("首页")
}说明:
NavigationStack管理导航栈。NavigationLink触发跳转。.navigationTitle()设置标题。
兼容旧版本:
NavigationView {
// ...
}iOS 16+ 推荐用
NavigationStack。如需兼容 iOS 15 及以下,用
NavigationView。
五、导航传值
通过初始化传值:
struct DetailView: View {
let name: String
var body: some View {
Text("详情:\(name)")
}
}
NavigationLink("查看详情") {
DetailView(name: "Hello")
}传递对象:
struct Item: Identifiable, Hashable {
let id = UUID()
let name: String
}
NavigationLink(value: item) {
Text(item.name)
}
.navigationDestination(for: Item.self) { item in
DetailView(item: item)
}说明:
navigationDestination根据值类型决定目标视图。适合列表跳转详情。
六、导航标题与工具栏
设置标题:
NavigationStack {
Text("内容")
.navigationTitle("页面标题")
}标题显示模式:
.navigationBarTitleDisplayMode(.inline)模式 | 说明 |
|---|---|
| 自动 |
| 小标题 |
| 大标题(默认) |
工具栏:
NavigationStack {
Text("内容")
.navigationTitle("页面")
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button("保存") {
// 操作
}
}
ToolbarItem(placement: .navigationBarLeading) {
Button("取消") {
// 操作
}
}
}
}常用位置:
位置 | 说明 |
|---|---|
| 导航栏左侧 |
| 导航栏右侧 |
| 底部 |
| 键盘上方 |
七、导航返回
默认返回按钮:
NavigationStack 自动生成返回按钮。
自定义返回:
@Environment(\.dismiss) var dismiss
Button("返回") {
dismiss()
}返回根视图:
@Environment(\.dismiss) var dismiss或使用 NavigationPath 管理:
@State private var path = NavigationPath()
NavigationStack(path: $path) {
// ...
}
Button("回到首页") {
path = NavigationPath()
}八、NavigationPath 多级跳转
@State private var path: [Item] = []
NavigationStack(path: $path) {
List(items) { item in
Button(item.name) {
path.append(item)
}
}
.navigationDestination(for: Item.self) { item in
DetailView(item: item)
}
}说明:
path管理导航栈。可以编程式跳转。
可以一次跳转多级。
九、标签页 TabView
底部标签栏用于多个主页面切换。
TabView {
HomeView()
.tabItem {
Label("首页", systemImage: "house")
}
SettingsView()
.tabItem {
Label("设置", systemImage: "gear")
}
}带选中状态:
@State private var selection = 0
TabView(selection: $selection) {
HomeView()
.tabItem {
Label("首页", systemImage: "house")
}
.tag(0)
SettingsView()
.tabItem {
Label("设置", systemImage: "gear")
}
.tag(1)
}十、综合示例:列表 → 详情
struct Item: Identifiable, Hashable {
let id = UUID()
let name: String
let description: String
}
struct ContentView: View {
let items = [
Item(name: "项目一", description: "描述一"),
Item(name: "项目二", description: "描述二"),
Item(name: "项目三", description: "描述三")
]
var body: some View {
NavigationStack {
List(items) { item in
NavigationLink(value: item) {
VStack(alignment: .leading) {
Text(item.name)
.font(.headline)
Text(item.description)
.font(.subheadline)
.foregroundColor(.gray)
}
}
}
.navigationTitle("列表")
.navigationDestination(for: Item.self) { item in
DetailView(item: item)
}
}
}
}
struct DetailView: View {
let item: Item
var body: some View {
VStack(spacing: 16) {
Text(item.name)
.font(.largeTitle)
Text(item.description)
.font(.body)
.foregroundColor(.gray)
}
.padding()
.navigationTitle(item.name)
.navigationBarTitleDisplayMode(.inline)
}
}十一、避坑指南
ForEach 的 id 必须唯一:否则界面可能异常。
NavigationLink 嵌套过深:注意导航栈管理。
NavigationStack 与 NavigationView 不要混用:同一层级只用一个。
navigationTitle 要放在 NavigationStack 内部:否则不生效。
列表行点击区域:用 Button 或 NavigationLink 包住整行。
性能问题:长列表使用 LazyVStack 或 List 自带懒加载。
@Environment(\.dismiss):子视图返回用 dismiss,不要手动 pop。
十二、检查清单
[ ] 会使用 List 展示数据
[ ] 掌握 ForEach 的用法
[ ] 能实现列表删除与移动
[ ] 会使用 NavigationStack 导航
[ ] 能实现页面跳转与传值
[ ] 会设置导航标题与工具栏
[ ] 会使用 TabView 做标签页
[ ] 能构建列表 → 详情的完整流程