List
List 是一种复杂的容器类型,可以通过为列表中的行提供单独的视图来构建列表,或者使用ForEach
来遍历一组数据来构建列表。您还可以混合这些策略,混合任意数量的单个视图和 ForEach
结构。
展示数据
-
创建一个结构体,并遵守
Identifiable
协议。 该结构体用于表示列表数据模型。1struct DataModel: Identifiable { 2 var id: UUID = UUID() 3 var title: String = "" 4 var subtitle: String = "" 5}
Important列表中的成员必须拥有唯一标识。唯一标识符允许SwiftUI为基础数据的变化自动生成动画,如插入、删除和移动。识别列表成员,要么遵守
Identifiable
协议,要么像 DataModel 一样,要么通过提供带有该类型唯一属性的关键路径的id参数。填充上述列表的ForEach取决于这种行为,列表初始化器也依赖于使用RandomAccessCollection
的成员进行迭代。 -
在 View 中声明一个数组变量,用户存放 DataModel 类型的元素。作为 ListView 展示内容的数据源。
1struct MyView: View { 2 var datas: [DataModel] = [ 3 DataModel(title: "My Title", subtitle:"subtitle"), 4 DataModel(title: "标题", subtitle: "子标题") 5 ] 6 var body: some View { ... } 7}
-
在视图的 body 中声明 ListView,并在其内部通过
ForEach
遍历列表的数据源 datas,并为每条数据声明 Text 来展示数据的 title1struct MyView: View { 2 var datas: [DataModel] = [ ... ] 3 var body: some View { 4 List { 5 ForEach(datas) { item in 6 Text(item.title) 7 } 8 } 9 } 10}
自定义行视图
在 List 内展示复杂内容,使用自定义 View 将更多的视图组合成更复杂的东西。随着行视图变得更加复杂,将视图重构为单独的视图结构,传递行需要渲染的数据。
-
创建一个自定视图 ListRowView,声明一个要渲染的数据变量,在内部的视图用数据进行渲染。
1struct ListRowView: View { 2 var model: DataModel 3 var body: some View { 4 VStack(alignment: .leading, spacing: 8){ 5 Text(model.title) 6 .font(.headline) 7 Label(model.subtitle, systemImage: "exclamationmark.circle") 8 .font(.subheadline) 9 } 10 } 11}
-
在 List 内的
ForEach
中使用自定义行视图 ListRowView 进行列表视图的渲染1struct MyView: View { 2 var datas: [DataModel] = [ ... ] 3 var body: some View { 4 List { 5 ForEach(datas) { item in 6 ListRowView(model: item) 7 } 8 } 9 } 10}
列表分组
列表视图还可以显示具有层次结构的数据,将相关数据分组到部分中。
-
创建一个 SectionDataModel 结构体,表示分组数据。items 属性表示分组所有行的数据。
1struct SectionDataModel : Identifiable { 2 var sectionId: UUID = UUID() 3 var sectionName: String = "" 4 var items: [DataModel] = [] 5}
-
将列表数据结构改造为二级数组结构,
1struct MyView: View { 2 var datas: [SectionDataModel] = [ 3 SectionDataModel(sectionName: "分组1", items: [ 4 DataModel(title: "My Title", subtitle: "subtitle"), 5 DataModel(title: "标题", subtitle: "子标题") 6 ]), 7 SectionDataModel(sectionName: "小池--杨万里", items: [ 8 DataModel(title: "泉眼无声惜细流", subtitle: "树阴照水爱晴柔"), 9 DataModel(title: "小荷才露尖尖角", subtitle: "早有蜻蜓立上头") 10 ]) 11 ] 12 var body: some View { ... } 13 }
-
使用
Section
视图为列表中的数据提供层次结构。1struct MyView: View { 2 var datas: [SectionDataModel] = [ ... ] 3 var body: some View { 4 List { 5 ForEach(datas) { section in 6 Section(header: Text(section.sectionName)) { 7 ForEach(section.items) { item in 8 ListRowView(model: item) 9 } 10 } 11 } 12 } 13 } 14}
列表导航
List 必须被包含 在 NavigationView
中, 使用 NavigationLink
导航至下一个 View。
通过用 NavigationView
包装列表来设置基于导航的用户界面。NavigationLink
的实例包裹列表的行,以提供目标视图,以便在用户点击该行时导航。
1NavigationView {
2 MyView()
3}
1struct MyView: View {
2 var datas: [SectionDataModel] = [ ... ]
3 var body: some View {
4 List {
5 ForEach(datas) { section in
6 Section(header: Text(section.sectionName)) {
7 ForEach(section.items) { item in
8 NavigationLink(destination: DetailView()) {
9 ListRowView(model: item)
10 }
11 }
12 }
13 }
14 }
15 }
16}
列表样式
调用 View 协议的 listStyle(_:)
方法来改变列表视图的展示样式。 ListStyle 有下面几种静态变量:
- automatic:描述平台默认行为和列表外观的列表样式。
- plain:描述普通列表行为和外观的列表样式。页眉和页脚将悬停在列表视图的顶部和底部。
- grouped:描述分组列表行为和外观的列表样式。在iOS上,分组列表样式显示比普通列表样式更大的页眉和页脚,这在视觉上使不同部分的成员保持距离。
- insetGrouped:描述嵌集分组列表的行为和外观的列表样式。在iOS上,内嵌分组列表样式显示连续的背景颜色,该颜色从部分标题延伸到部分中列表项的两侧,一直延伸到部分页脚。这在视觉上将项目分组到比内嵌或分组样式更大的程度。
1List {
2 ForEach(datas) { item in
3 ListRowView(model: item)
4 }
5}.listStyle(.automatic)
行内样式
- 使用
listRowInsets(_:)
更改列表中行内容的边距。 - 使用
listItemTint(_:)
设置行内特定内容关联的固定色调。 - 使用
listRowBackground(_:)
设置行的自定义背景视图。 - 使用
badge(_:)
在行内右侧展示徽章。设置徽章会导致行内背景失效。
1...
2List {
3 ForEach(datas) { item in
4 ListRowView(model: item)
5 .listRowInsets(.init(top: 8, leading: 8, bottom: 8, trailing: 8))
6 .listItemTint(.green)
7 .listRowBackground(Color.purple.opacity(0.1))
8 .badge("❤️")
9 }
10}
11...
分割线样式
- 使用
listRowSeparatorTint(_:edges:)
设置分割线颜色 - 使用
listRowSeparator(_:edges:)
设置分割线显示隐藏 - 使用
listSectionSeparatorTint(_:edges:)
设置分组分割线颜色 - 使用
listSectionSeparator(_:edges:)
设置分组分割线显示隐藏
1List {
2 Section(header: Text("分组标题")){
3 ListRowView()
4 .listRowSeparatorTint(.yellow, edges: .all)
5 .listRowSeparator(.hidden, edges: .all)
6 }
7 .listSectionSeparatorTint(.cyan, edges: .all)
8 .listSectionSeparator(.hidden, edges: .top)
9}
侧滑操作
使用 swipeActions(edge:allowsFullSwipe:content:)
将自定义滑动操作添加到列表中的行中。
方法定义
1func swipeActions<T>(
2 edge: HorizontalEdge = .trailing,
3 allowsFullSwipe: Bool = true,
4 @ViewBuilder content: () -> T
5) -> some View where T : View
参数说明
- edge: 侧滑操作的起始位置。默认值是 HorizontalEdge.trailing。
.leading
从左侧开始滑动,.trailing
从右侧开始滑动。 - allowsFullSwipe: 完全侧滑是否自动执行第一个操作。默认值为 true。
- content: 滑动操作的内容。一般为 Button 控件。
使用示例
1List {
2 ListRowView()
3 .swipeActions(edge: .leading, allowsFullSwipe: true) {
4 Button {
5 debugPrint("Read Action")
6 } label: {
7 Label("Read", systemImage: "envelope.open")
8 }
9 .tint(.blue)
10 Button {
11 debugPrint("Unread Action")
12 } label: {
13 Label("Unread", systemImage: "envelope.badge")
14 }
15 }
16}