SwiftUI 入门教程 - List

1,482 阅读1分钟

前言

List 是 SwiftUI 官方提供的一个组件,它用来展示多行单列的数据。类似于 UIKit 中的 UITableView,但它的使用要比前者更加简单、更加优雅。并且它提供了选择单个或者多个数据的能力。下面是它最简单的使用方式:

var body: some View {
    List {
        Text("第一条数据")
        Text("第二条数据")
        Text("第三条数据")
        Text("第四条数据")
    }
}

效果图如下:

截屏2023-08-22 08.35.49.png

支持动态数据

在我们日常开发中,很少有静态数据的列表,更多的情况是请求接口拿到数据再填充到表格中。下面是一个动态数据的例子,主要通过三个步骤来实现:

  • 定义一个结构体来表示数据格式
  • 声明一个变量来存储数据源
  • 将数据源填充到 List 中来进行展示

代码如下:

// 第一步
struct Food: Identifiable {
    let name: String
    let id = UUID()
}

struct ListTest: View {
	// 第二步
    let foods = [Food(name: "Hamburg"), Food(name: "Chicken"), Food(name: "Fish"), Food(name: "Beef")]
    
    var body: some View {
    	// 第三步
        List {
            ForEach(foods) { food in
                Text(food.name)
            }
        }
    }
}

因为 List 的构造器本身支持了 ForEach 的功能,所以 List 的代码可以简化成下面的:

var body: some View {
    List(foods){
        Text($0.name)
    }
}

Tips: 定义的数据结构需要遵守 Identifiable

带有 Section 的列表

对于需要分 Section 的列表,我们可以借助 ForEach 和 Section 来实现。示例代码如下:

struct FoodSection: Identifiable {
    let type: String
    let foods: [Food]
    let id = UUID()
}

struct ListTest: View {
    let sectionFoods = [        FoodSection(type: "主食", foods: [Food(name: "Hamburg"), Food(name: "Pizza")]),
        FoodSection(type: "小食", foods: [Food(name: "Chicken"), Food(name: "Fish")])
    ]
    
    var body: some View {
        List {
            ForEach(sectionFoods) { section in
                Section(content: {
                    ForEach(section.foods) { food in
                        Text(food.name)
                    }
                }, header: {
                    Text("Title \(section.type)")
                })
            }
        }
    }
}

首先,我们声明一个 FoodSection 的结构体来表示分区数据源的数据结构。接着,我们声明一个 sectionFoods 的常量来存储需要展示的数据。最后,在 List 中使用 ForEach 对 sectionFoods 进行遍历,使用 Section 组件,在 content 里面遍历 foods 字段进行食物名称的展示。然后取出每个 section 的 type 字段赋值给 header。

这样,就可以展示具有分区的数据列表了。

效果图如下:

截屏2023-08-22 09.18.29.png

支持单元格的单选和多选

如果要实现单元格的单选或者多选,我们需要声明一个 @State 修饰的变量来存储已选择的数据,然后使用 List 的这个初始化构造器:init(_:selection:rowContent:)。其它步骤和上述的基本使用并无差别:

struct ListTest: View {
	let foods = [Food(name: "Hamburg"), Food(name: "Chicken"), Food(name: "Fish"), Food(name: "Beef")]
    @State private var selections = Set<UUID>()
    var body: some View {
        NavigationStack {
            List(foods, selection: $selections) {
                Text($0.name)
            }
            .navigationTitle("菜单")
            .toolbar {
                EditButton()
            }
        }
        Text("选择了\(selections.count)种食物")
    }
}

为了演示效果,给 List 的外层包裹了 NavigationStack,对其不了解的可以看这里

效果如下: 屏幕录制2023-08-22-08.56.18.gif

需要注意的是,该效果需要运行代码在模拟器中看,在实时预览的页面是看不到这个动态效果的。