Lists
SwiftUI Lists 组件的全面学习笔记,涵盖基础用法、数据驱动、选择、层级结构、编辑和样式定制
概述
List 是 SwiftUI 中用于展示单列数据行的容器视图,支持选择一个或多个成员。它是构建数据列表界面的核心组件,适用于所有 Apple 平台(iOS 13.0+、macOS 10.15+、watchOS 6.0+、tvOS 13.0+、visionOS 1.0+)。
基础用法
静态列表
最简单的 List 使用静态内容创建:
var body: some View {
List {
Text("第一项")
Text("第二项")
Text("第三项")
}
}数据驱动列表
使用 Identifiable 数据
当数据类型遵循 Identifiable 协议时,可以直接创建列表:
struct Ocean: Identifiable {
let name: String
let id = UUID()
}
private var oceans = [
Ocean(name: "太平洋"),
Ocean(name: "大西洋"),
Ocean(name: "印度洋"),
Ocean(name: "南冰洋"),
Ocean(name: "北冰洋")
]
var body: some View {
List(oceans) { ocean in
Text(ocean.name)
}
}使用 KeyPath 指定标识符
对于不遵循 Identifiable 的数据,可以通过 id 参数指定标识符的 KeyPath:
struct Country {
let name: String
let code: String
}
private var countries = [
Country(name: "中国", code: "CN"),
Country(name: "美国", code: "US")
]
var body: some View {
List(countries, id: \.code) { country in
Text(country.name)
}
}选择支持
单选
通过绑定单个 ID 值实现单选:
struct Ocean: Identifiable, Hashable {
let name: String
let id = UUID()
}
@State private var singleSelection: UUID?
var body: some View {
NavigationView {
List(oceans, selection: $singleSelection) { ocean in
Text(ocean.name)
}
.navigationTitle("海洋")
.toolbar { EditButton() }
}
}多选
通过绑定 Set 类型实现多选:
@State private var multiSelection = Set<UUID>()
var body: some View {
NavigationView {
List(oceans, selection: $multiSelection) { ocean in
Text(ocean.name)
}
.navigationTitle("海洋")
.toolbar { EditButton() }
}
Text("\(multiSelection.count) 个选中项")
}注意:在 iOS 和 iPadOS 中,需要在编辑模式下才能进行选择操作。使用
EditButton或通过环境值editMode控制编辑状态。
层级列表
创建多维列表
使用 children 参数创建支持展开/折叠的层级列表:
struct FileItem: Hashable, Identifiable, CustomStringConvertible {
var id: Self { self }
var name: String
var children: [FileItem]? = nil
var description: String {
switch children {
case nil:
return "📄 \(name)"
case .some(let children):
return children.isEmpty ? "📂 \(name)" : "📁 \(name)"
}
}
}
private var fileHierarchyData: [FileItem] = [
FileItem(name: "users", children: [
FileItem(name: "user1234", children: [
FileItem(name: "Photos", children: [
FileItem(name: "photo001.jpg")
]),
FileItem(name: "Movies", children: [
FileItem(name: "movie001.mp4")
])
])
]),
FileItem(name: "private", children: nil)
]
var body: some View {
List(fileHierarchyData, children: \.children) { item in
Text(item.description)
}
}列表编辑
启用编辑操作
使用 editActions 参数启用删除和移动操作:
@State private var items = ["项目 1", "项目 2", "项目 3"]
var body: some View {
NavigationView {
List($items, id: \.self, editActions: [.delete, .move]) { $item in
Text(item)
}
.navigationTitle("可编辑列表")
.toolbar { EditButton() }
}
}自定义删除操作
使用 onDelete 修饰符自定义删除行为:
List {
ForEach(items, id: \.self) { item in
Text(item)
}
.onDelete { indexSet in
items.remove(atOffsets: indexSet)
}
}自定义移动操作
使用 onMove 修饰符自定义移动行为:
List {
ForEach(items, id: \.self) { item in
Text(item)
}
.onMove { source, destination in
items.move(fromOffsets: source, toOffset: destination)
}
}列表样式
内置样式
SwiftUI 提供多种列表样式,使用 listStyle(_:) 修饰符应用:
// 默认样式(根据平台自动选择)
List { ... }
.listStyle(.automatic)
// 纯净样式
List { ... }
.listStyle(.plain)
// 分组样式
List { ... }
.listStyle(.grouped)
// 内嵌样式(macOS)
List { ... }
.listStyle(.inset)
// 侧边栏样式
List { ... }
.listStyle(.sidebar)
// 边框样式(macOS)
List { ... }
.listStyle(.bordered)
// 椭圆样式(watchOS)
List { ... }
.listStyle(.elliptical)
// 轮播样式(watchOS)
List { ... }
.listStyle(.carousel)交替行背景
在 macOS 上,某些样式支持交替行背景:
List { ... }
.listStyle(.inset(alternatesRowBackgrounds: true))
List { ... }
.listStyle(.bordered(alternatesRowBackgrounds: true))分组内容
使用 Section
使用 Section 将列表内容分组:
List {
Section(header: Text("水果")) {
Text("苹果")
Text("香蕉")
}
Section(header: Text("蔬菜")) {
Text("胡萝卜")
Text("西兰花")
}
}带页眉和页脚的分组
List {
Section {
Text("项目 1")
Text("项目 2")
} header: {
Text("页眉")
} footer: {
Text("页脚说明文字")
}
}常用修饰符
行外观定制
// 自定义行背景
List(items, id: \.self) { item in
Text(item)
.listRowBackground(Color.blue.opacity(0.1))
}
// 隐藏行分隔符
List(items, id: \.self) { item in
Text(item)
.listRowSeparator(.hidden)
}
// 自定义行分隔符颜色
List(items, id: \.self) { item in
Text(item)
.listRowSeparatorTint(.red)
}
// 自定义行内边距
List(items, id: \.self) { item in
Text(item)
.listRowInsets(EdgeInsets(top: 10, leading: 20, bottom: 10, trailing: 20))
}列表级别定制
// 自定义列表行背景
List { ... }
.listRowBackground(Color.clear)
// 隐藏滚动指示器
List { ... }
.scrollIndicators(.hidden)
// 控制滚动禁用
List { ... }
.scrollDisabled(true)性能优化
使用 LazyVStack vs List
对于简单场景,List 已经进行了懒加载优化。但在某些情况下,LazyVStack 可能提供更多控制:
// List 自动懒加载
List(0..<1000) { index in
Text("行 \(index)")
}
// 显式使用 LazyVStack
ScrollView {
LazyVStack {
ForEach(0..<1000) { index in
Text("行 \(index)")
}
}
}标识符选择
确保为列表项提供稳定且唯一的标识符,避免使用数组索引作为 ID:
// ❌ 不推荐:使用索引
List(items.indices, id: \.self) { index in
Text(items[index])
}
// ✅ 推荐:使用稳定的唯一标识符
List(items, id: \.id) { item in
Text(item.name)
}平台差异
iOS/iPadOS
- 默认样式为
.insetGrouped - 选择需要编辑模式
- 支持滑动删除
macOS
- 默认样式为
.inset - 支持直接点击选择
- 支持交替行背景
- 支持边框样式
watchOS
- 提供
.elliptical和.carousel特殊样式 - 自动适应圆形表盘
tvOS
- 优化焦点导航
- 支持遥控器交互
最佳实践
- 使用 Identifiable:让数据模型遵循
Identifiable协议,简化列表创建 - 避免过度嵌套:保持列表行视图简洁,复杂视图提取为独立组件
- 合理使用 Section:通过分组提高可读性
- 选择合适的样式:根据平台和设计需求选择列表样式
- 性能考虑:对于大数据集,确保使用稳定的标识符
- 编辑模式:在 iOS 上记得提供进入编辑模式的方式(如
EditButton)
总结
List 是 SwiftUI 中功能强大且灵活的列表组件,支持静态内容、数据驱动、选择、层级结构和编辑等多种场景。通过合理使用样式和修饰符,可以创建符合平台规范且用户体验优秀的列表界面。
上次更新于