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}