List — 什么是 SwiftUI 中的列表组件

作者: IT Sectr 发布日期: 2026-06-25 阅读时间: 7 分钟

List — 是 SwiftUI 中的一个容器 View,用于以垂直可滚动列表的形式显示数据,相当于 UIKit 中的 UITableView。根据 Apple Developer Documentation, 2024List 支持静态和动态分区、滑动操作、行拖放和 pull-to-refresh。与 UITableView 不同,List 使用基于 SwiftUI 和 ForEach 的声明式 API,自动管理单元格复用和大量行时的性能。

要点

  • List — SwiftUI 中可滚动数据列表的容器
  • ForEach — 在 List 中动态显示数据的主要方式
  • 分区 — 用于分组带标题行的 Section View
  • 滑动操作 — iOS 15+ 的 swipeActions
  • Pull-to-refresh — iOS 15+ 的 .refreshable

SwiftUI 中的 List 是什么?

List — 是一个 View,用于在垂直可滚动列表中显示元素序列。它随 iOS 13 和 SwiftUI 一起出现,是显示数据列表的主要方式,取代了 UIKit 中的 UITableView。List 自动管理单元格复用、滚动和性能。

List 使用懒加载:单元格在滚动时创建,而不是一次性全部创建。这将其与 ScrollView 内使用 ForEach 的 VStack 区分开来,后者所有单元格都在渲染时创建。List 还提供对滑动操作、pull-to-refresh、编辑(删除/移动)和行选择的内置支持。

根据 Apple WWDC 2021 (Session 10072),iOS 15+ 中的 List 由于新的集合级 diffing 机制,获得了显著的性能改进。这使得 List 在更新数据时更加高效,特别是对于数百行的列表。

List 与 ScrollView + VStack 的比较

开发人员经常在 List 和 ScrollView 与 VStack 之间选择来显示一组 Views。关键区别:List 使用单元格复用(如 UITableView),而 ScrollView + VStack 一次性创建所有 Views。对于固定数量元素(最多 20 个)的列表,差异可以忽略不计。对于 50+ 行的动态列表,List 在性能方面更优。

静态和动态列表

静态 List — 是具有固定行数的列表,行数直接在 List 主体中指定。用于菜单、设置和具有已知元素集的表单。每行都显式声明,无需循环或 ForEach。

swift
// 静态列表(用于菜单和设置)
List {
    Text("个人资料")
    Text("设置")
    Text("关于")
}

// 动态列表(用于数据)
struct UserList: View {
    let users: [User]

    var body: some View {
        List(users) { user in
            HStack {
                Text(user.name)
                Text(user.role)
                    .foregroundColor(.secondary)
            }
        }
    }
}

动态 List 使用 List(data:rowContent:) 初始化器或 List 主体内的 ForEach。第一种方式在每个行对应一个数据元素时很方便。第二种方式在数据之间有分区或额外元素时使用。

标识 (Identifiable): 对于动态列表,数据元素必须符合 Identifiable 协议,或者在 data:id 元组中指定指向唯一标识符的 KeyPath。SwiftUI 使用标识符跟踪变化:添加、删除和移动行。

分区和数据分组

Section — 是一个 View,用于在 List 中分组行,带有标题和可选的页脚。Section 将 header 和 footer 作为 ViewBuilder 接受,允许不仅使用文本,还可以使用自定义 Views 作为分区标题。

swift
struct SettingsView: View {
    var body: some View {
        List {
            Section(header: Text("账户")) {
                Text("姓名")
                Text("电子邮件")
            }
            Section(header: Text("通知")) {
                Toggle("Push", isOn: $pushEnabled)
                Toggle("Email", isOn: $emailEnabled)
            }
        }
        .listStyle(.insetGrouped)
    }
}

// 使用 ForEach 的动态分区
List {
    ForEach(groupedData.keys.sorted(), id: \.self) { key in
        Section(header: Text(key)) {
            ForEach(groupedData[key]!) { item in
                Text(item.title)
            }
        }
    }
}

列表样式: SwiftUI 通过 .listStyle() 修饰符提供几种内置样式。.insetGrouped — iOS Settings 的标准样式,.plain — 极简风格,.inset — 带缩进,.sidebar — 用于 iPad 上的 Sidebar。

根据 SwiftUI Cookbook (2024),带有动态分区和内部 ForEach 的 Section 是复杂结构应用程序中数据分组的标准模式。关键规则:不要将 Section 嵌套在 Section 内,也不要将 List(data:) 初始化器与 Section 一起使用 — 在 List 主体内使用 ForEach。

滑动操作和 pull-to-refresh

.swipeActions(edge:allowsFullSwipe:content:) — iOS 15+ 的修饰符,为 List 行添加滑动操作。允许在向左(默认)或向右滑动时显示按钮,具有不同的颜色和角色(destructive, cancel)。

swift
struct TaskList: View {
    @Binding var tasks: [Task]

    var body: some View {
        List {
            ForEach($tasks) { $task in
                Text(task.title)
                    .swipeActions(edge: .trailing) {
                        Button("删除", role: .destructive) {
                            tasks.removeAll { $0.id == task.id }
                        }
                    }
                    .swipeActions(edge: .leading) {
                        Button(task.isDone ? "撤销" : "完成") {
                            task.isDone.toggle()
                        }
                        .tint(.green)
                    }
            }
        }
        .refreshable {
            // 异步数据加载
            await loadTasks()
        }
    }
}

.refreshable — iOS 15+ 的修饰符,添加 pull-to-refresh。接受一个异步闭包 (async),在用户下拉列表时执行。SwiftUI 自动显示加载指示器。操作完成后指示器隐藏。

.onDelete 和 .onMove — iOS 13+ 的修饰符,添加对删除和移动行的支持。要使用它们,将数据包装在带 Binding 的 ForEach 中,或通过 List 或 ForEach 上的 .onDelete(perform:) 传递闭包。

List 性能和优化

List 性能 取决于行数、每个单元格的复杂度和数据更新频率。SwiftUI 使用懒加载和单元格复用(类似于 UITableView.dequeueReusableCell),但对于 500+ 行的列表可能需要额外优化。

优化描述iOS 版本
Identifiable每个元素的唯一 IDiOS 13+
EquatableView在数据相等时避免重新渲染iOS 13+
id(_:)ID 更改时强制重新创建 ViewiOS 13+
.equatable()严格按 Equatable 比较iOS 15+
Diffable data更改时自动 diffiOS 15+

问题 1:频繁更新。 如果列表中的数据频繁更新(例如每秒),List 可能会在每次状态更改时重新渲染可见单元格。解决方案:对数据使用结构体(value types)—— SwiftUI 按值比较它们,仅重新渲染更改过的行。

问题 2:重型单元格。 如果每行包含复杂的 View 层次结构、图像和动画,滚动可能会变慢。解决方案:将单元格移动到单独的 Views 中,使用 EquatableView 防止不必要的重新渲染。根据 SwiftUI Lab (2024),将复杂行拆分为子组件可将渲染时间减少 30–50%。

问题 3:大量行。 在 1000+ 行时,List 由于懒加载仍然高效工作,但初始加载可能因布局计算而变慢。解决方案:仅对不需要 List 功能(滑动、分区)的同类行列表使用 LazyVStack。对于全功能列表,List 仍然是最佳选择。

常见问题

SwiftUI 中的 List 是什么?

List — 用于在 SwiftUI 中显示可滚动数据列表的容器 View。相当于 UIKit 中带声明式 API 的 UITableView。支持分区、滑动操作、pull-to-refresh、编辑和通过 .listStyle() 自定义。

List 与 ScrollView + VStack 有何不同?

List 使用 懒加载和单元格复用 — 单元格在滚动时创建。ScrollView + VStack 一次性创建所有 Views。对于 50+ 行的列表,List 更优。对于固定的小集合(最多 20 个元素),差异可以忽略不计。

如何向 List 添加 pull-to-refresh?

使用 .refreshable (iOS 15+) 修饰符。传递一个带有数据更新逻辑的异步闭包。SwiftUI 自动显示加载指示器,并在异步操作完成后隐藏它。

如何在 List 中分组行?

使用带标题和可选页脚的 Section View。将列表行放在 Section 内部。对于动态分区,使用带 groupedData 的 ForEach。列表样式通过 .listStyle(.insetGrouped) 配置以获得类似 iOS 的外观。

如何加速具有大量行的 List?

对数据使用 结构体(value types),将复杂单元格移动到带 EquatableView 的单独 Views 中,避免每行的频繁状态更新。对于 1000+ 行的列表,如果不需要 List 功能,请考虑使用 LazyVStack。

总结

  • List — 具有懒加载和单元格复用的可滚动列表容器
  • ForEach — 在 List 中动态显示数据的主要方式
  • Section — 带标题和页脚的行分组
  • swipeActions — iOS 15+ 的滑动操作
  • refreshable — iOS 15+ 的 pull-to-refresh
  • 样式 — insetGrouped, plain, inset, sidebar 通过 .listStyle()
  • 性能 — 使用 Identifiable、EquatableView 和 value types

我们将开发一款交钥匙移动应用程序

IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。

讨论项目

另请阅读