文章目录
-
- 一、简介
- 二、基本用法
-
- [2.1 静态数据](#2.1 静态数据)
- [2.2 动态数据](#2.2 动态数据)
-
- [2.2.1 直接使用 List 初始化](#2.2.1 直接使用 List 初始化)
- [2.2.2 使用 List 和 ForEach](#2.2.2 使用 List 和 ForEach)
- 三、常用属性
-
- [3.1 显示/隐藏分割线](#3.1 显示/隐藏分割线)
- [3.2 设置背景行颜色](#3.2 设置背景行颜色)
- [3.3 列表样式](#3.3 列表样式)
- [3.4 删除](#3.4 删除)
- [3.5 排序](#3.5 排序)
- [3.6 下拉刷新](#3.6 下拉刷新)
- [3.7 设置标题](#3.7 设置标题)
- [3.8 多选或单选](#3.8 多选或单选)
-
- [3.8.1 多选](#3.8.1 多选)
- [3.8.2 单选](#3.8.2 单选)
- [3.8.3 注意事项](#3.8.3 注意事项)
- 四、综合案例
一、简介
在 SwiftUI 中,List 组件用于创建可滚动的列表界面。它类似于UIKit中的 UITableView,但使用起来更加简洁和直观。List 可以显示静态或动态的数据,并支持多种样式和交互。
二、基本用法
2.1 静态数据
List 可以包含一系列静态的视图元素,这些元素会按照添加的顺序依次显示在列表中。
swift
List {
Text("第一项")
Text("第二项")
Text("第三项")
}

在这个例子中,我们创建了一个简单的列表,其中包含三个Text视图。每个 Text 视图代表列表中的一行。
2.2 动态数据
通常,我们会使用动态的数据集合来填充列表。List 可以与 Swift 的数组、集合等数据结构结合使用。
2.2.1 直接使用 List 初始化
以下是一个使用数组创建动态列表的示例:
swift
import SwiftUI
struct ContentView: View {
let items = ["苹果", "香蕉", "橙子", "葡萄"]
var body: some View {
List(items, id: \.self) { item in
Text(item)
}
}
}

在这个示例中,items 是一个字符串数组,id: \.self 表示使用数组元素本身作为唯一标识符。闭包{ item in Text(item) }定义了列表中每一行的显示内容,这里是将数组中的每个元素以Text视图的形式显示出来。
特点:
- 简洁性: 这种方式是直接将数据数组传递给
List,然后使用闭包来定义每个列表项的视图。它更为简洁,适合简单的列表。 - 自动识别:
SwiftUI会自动识别数据并为每个元素创建一个列表项。 - 适用场景: 适用于简单的、单一数据源的列表。
2.2.2 使用 List 和 ForEach
ForEach 用于在 List 中迭代显示动态数据。
swift
struct DynamicView: View {
let items = ["苹果", "香蕉", "橙子", "葡萄"]
var body: some View {
List{
ForEach(items,id:\.self){ item in
Text(item)
}
}
}
}
特点:
- 灵活性: 这种方式通过
ForEach显式地遍历数据数组,并在 List 中构建每个列表项。这提供了更大的灵活性。 - 组合内容: 在
List中使用ForEach可以更方便地与其他视图组合,比如在同一个列表中添加静态视图、Section、或者其他动态内容。 - 适用场景: 适用于复杂的列表视图,特别是当你需要在列表中混合使用不同类型的内容时。
三、常用属性
3.1 显示/隐藏分割线
默认情况下,List 会显示分隔线来区分每一行。如果你想隐藏分隔线,可以使用 listRowSeparator 修饰符。
swift
struct DynamicView: View {
let items = ["苹果", "香蕉", "橙子", "葡萄"]
var body: some View {
List(items, id: \.self) { item in
Text(item)
.listRowSeparator(.hidden)
}
}
}

3.2 设置背景行颜色
使用 listRowBackground 修饰符为列表中的每一行设置背景颜色。
swift
struct DynamicView: View {
let items = ["苹果", "香蕉", "橙子", "葡萄"]
var body: some View {
List(items, id: \.self) { item in
Text(item)
.listRowSeparator(.hidden)
.listRowBackground(Color.gray.opacity(0.2))
}
}
}

3.3 列表样式
List 支持多种不同的样式,你可以使用listStyle修饰符来设置。常见的列表样式:
DefaultListStyle: 使用系统默认的列表样式。这是List的默认样式。PlainListStyle: 提供一个简单的列表样式,没有分隔线和背景。GroupedListStyle: 将列表项目分组,通常用于设置界面中的分节列InsetGroupedListStyle: 类似于GroupedListStyle,但项目分组时有内边距。这种样式在iOS上看起来与设置应用中的列表样式类似。InsetListStyle: 列表项有内边距,但不分组。SidebarListStyle: 专为侧边栏设计的列表样式,通常在macOS和iPadOS的多列布局中使用。
以PlainListStyle为例:
swift
struct DynamicView: View {
let items = ["苹果", "香蕉", "橙子", "葡萄"]
var body: some View {
List(items, id: \.self) { item in
Text(item)
}
.listStyle(PlainListStyle())
}
}

3.4 删除
onDelete支持在列表中删除元素的功能。
swift
struct DynamicView: View {
@State var items = ["苹果", "香蕉", "橙子", "葡萄"]
var body: some View {
List{
ForEach(items,id:\.self){ item in
Text(item)
}
.onDelete(perform: deleteItems)
}
}
// 删除元素
func deleteItems(at offsets:IndexSet){
items.remove(atOffsets: offsets)
}
}

解释:
func deleteItems(at offsets: IndexSet)at: 这是外部参数名。在函数调用时,使用这个名称来指示参数的目的或位置。它帮助使函数调用更加可读和清晰。offsets: 这是内部参数名。在函数实现中使用这个名称来引用传递给函数的IndexSet值。
3.5 排序
onMove支持在列表中移动元素的功能
swift
struct DynamicView: View {
@State var items = ["苹果", "香蕉", "橙子", "葡萄"]
var body: some View {
List{
ForEach(items,id:\.self){ item in
Text(item)
}
.onDelete(perform: deleteItems)
.onMove(perform: moveItems)
}
}
// 删除元素
func deleteItems(at offsets:IndexSet){
items.remove(atOffsets: offsets)
}
// 移动元素
func moveItems(from source:IndexSet, to destination:Int){
items.move(fromOffsets: source, toOffset: destination)
}
}

3.6 下拉刷新
refreshable支持下拉刷新功能。
swift
struct ListDemoView: View {
@State var items = ["张三","李四","王五","赵六"]
var body: some View {
VStack{
List(items,id: \.self) {item in
Text(item)
}
.refreshable {
await fetchData()
}
}
}
func fetchData() async{
await Task.sleep(2 * 1_000_000_000) // 2 seconds
self.items.append("Orange")
}
}


3.7 设置标题
在SwiftUI中,navigationTitle 是一个用于设置导航视图中标题的修饰符。这个修饰符通常与 NavigationView 一起使用,以为视图提供一个标题,显示在导航栏的顶部。
navigationTitle 修饰符可以应用于List或其他视图,以设置当前视图的标题。当视图被嵌入在NavigationView中时,navigationTitle 会在导航栏中显示。
swift
struct TitleListView: View {
let items = ["Red", "Green", "Blue"]
var body: some View {
NavigationView {
List(items, id: \.self) { item in
Text(item)
}
.toolbar{
EditButton()
}
.navigationTitle("Colors")
}
}
}

注意 :navigationTitle 只有在视图被嵌套在NavigationView中时才会生效。否则,标题不会显示。
3.8 多选或单选
在 SwiftUI 中,List 组件的 selection 属性用于支持多选或单选功能。通过 selection 属性,我们可以跟踪用户在列表中选择的项。这个功能通常与 EditMode 结合使用,以便用户可以进入编辑模式进行选择。
selection 属性需要绑定到一个状态变量,该变量可以是 Set 类型(用于多选)或单个可选值(用于单选)。当用户在列表中选择或取消选择项时,绑定的变量会自动更新。
3.8.1 多选
swift
struct TitleListView: View {
let items = ["Red", "Green", "Blue"]
@State private var selectedItems = Set<String>()
var body: some View {
NavigationView {
List(items, id: \.self, selection: $selectedItems) { item in
Text(item)
}
.toolbar{
EditButton()
}
.navigationTitle("选择颜色")
}
}
}

3.8.2 单选
如果希望实现单选功能,可以将 selectedItems 改为一个可选值:
swift
struct TitleListView: View {
let items = ["Red", "Green", "Blue"]
@State private var selectedItem: String? = nil
var body: some View {
NavigationView {
List(items, id: \.self, selection: $selectedItem) { item in
Text(item)
}
.toolbar{
EditButton()
}
.navigationTitle("选择颜色")
}
}
}

3.8.3 注意事项
- 编辑模式:
selection属性的功能通常在列表进入编辑模式后才有效。EditButton是一个简单的方法来切换编辑模式. - 绑定类型: 对于多选,
selection应绑定到一个Set类型的变量;对于单选,selection应绑定到一个可选值。 - 唯一标识: 列表项需要有唯一标识符(例如通过
id: \.self),以便SwiftUI能够正确跟踪选择状态。
四、综合案例
以水果CRUD为例,如下:
swift
import SwiftUI
struct ContentView: View {
@State private var fruits = ["苹果", "香蕉", "橙子", "葡萄"]
@State private var selectedFruits = Set<String>()
@State private var searchText = ""
var body: some View {
NavigationView {
VStack {
// 搜索框
TextField("搜索水果", text: $searchText)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
// 列表
List(selection: $selectedFruits) {
ForEach(filteredFruits, id: \.self) { fruit in
Text(fruit)
}
.onDelete(perform: deleteFruits)
.onMove(perform: moveFruits)
}
.listStyle(InsetGroupedListStyle())
.navigationTitle("水果列表")
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
EditButton()
}
ToolbarItem(placement: .navigationBarTrailing) {
Button(action: addFruit) {
Image(systemName: "plus")
}
}
}
}
}
}
// 过滤水果
var filteredFruits: [String] {
if searchText.isEmpty {
return fruits
} else {
return fruits.filter { $0.contains(searchText) }
}
}
// 添加水果
func addFruit() {
fruits.append("新水果")
}
// 删除水果
func deleteFruits(at offsets: IndexSet) {
fruits.remove(atOffsets: offsets)
}
// 移动水果
func moveFruits(from source: IndexSet, to destination: Int) {
fruits.move(fromOffsets: source, toOffset: destination)
}
}
#Preview {
ContentView()
}


代码说明
-
搜索功能:
- 使用
TextField来实现简单的搜索功能,用户可以输入文本来过滤水果列表。 filteredFruits计算属性根据searchText返回过滤后的水果列表。
- 使用
-
列表功能:
List(selection:): 支持多选功能,绑定到selectedFruits。ForEach: 用于迭代filteredFruits,并为每个水果创建一个文本视图。.onDelete和.onMove:支持删除和重新排序水果。.listStyle: 使用InsetGroupedListStyle来设置列表的外观。
-
导航和工具栏:
NavigationView和navigationTitle: 为列表提供导航栏和标题。EditButton和addFruit: 提供编辑和添加新水果的功能。