SwiftUI基础组件之List详解

文章目录

    • 一、简介
    • 二、基本用法
      • [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: 提供编辑和添加新水果的功能。
相关推荐
MaoJiu14 小时前
Flutter造轮子系列:flutter_permission_kit
flutter·swiftui
qq_4335545417 小时前
C++ list代码练习、set基础概念、set对象创建、set大小操作
开发语言·c++·list
IGP919 小时前
20250606-C#知识:List排序
c#·list
大熊猫侯佩20 小时前
由一个 SwiftData “诡异”运行时崩溃而引发的钩深索隐(三)
数据库·swiftui·swift
大熊猫侯佩20 小时前
由一个 SwiftData “诡异”运行时崩溃而引发的钩深索隐(二)
数据库·swiftui·swift
大熊猫侯佩20 小时前
用异步序列优雅的监听 SwiftData 2.0 中历史追踪记录(History Trace)的变化
数据库·swiftui·swift
大熊猫侯佩20 小时前
由一个 SwiftData “诡异”运行时崩溃而引发的钩深索隐(一)
数据库·swiftui·swift
大熊猫侯佩2 天前
SwiftUI 中如何花样玩转 SF Symbols 符号动画和过渡特效
swiftui·swift·apple
大熊猫侯佩2 天前
SwiftData 共享数据库在 App 中的改变无法被 Widgets 感知的原因和解决
swiftui·swift·apple
大熊猫侯佩2 天前
SwiftUI 在 iOS 18 中的 ForEach 点击手势逻辑发生改变的解决
swiftui·swift·apple