SwiftUI — ForEach

ForEach 是 SwiftUI 中用于动态生成一系列视图的控制结构。它非常灵活,可以用在各种容器(如 VStack, HStack, ZStack, List 等)中,帮助你通过一组数据动态地渲染一组视图。

1. 基本用法 #

ForEach 的基本用法是通过一个集合(数组、范围等)来创建一组重复的视图。它会为集合中的每个元素生成一个对应的视图。

例子: #

struct ContentView: View {
    let items = ["Apple", "Banana", "Cherry"]
    
    var body: some View {
        VStack {
            ForEach(items, id: \.self) { item in
                Text(item)
                    .padding()
            }
        }
    }
}
  • 解释
    • ForEach(items, id: \.self)items 是一个数组,id: \.self 表示数组中的每个元素(这里是 String 类型)作为每个视图的唯一标识符。
    • item in:对于 items 数组中的每一个元素,item 表示当前元素。
    • Text(item):为每个 item 创建一个 Text 视图。

输出: #

  • 会在垂直堆叠(VStack)中显示三个 Text 视图,分别显示 AppleBananaCherry

2. 使用范围生成视图 #

ForEach 还可以用于生成一系列基于范围的数据。例如,生成一个从 0 到 9 的数字列表。

例子: #

struct ContentView: View {
    var body: some View {
        VStack {
            ForEach(0..<10) { number in
                Text("Number \(number)")
                    .padding()
            }
        }
    }
}
  • 解释
    • ForEach(0..<10):这里使用的是一个范围(0..<10),它表示从 0 到 9(不包括 10)。
    • 对于每一个数字,Text("Number \(number)") 会生成一个对应的文本视图。

3. 使用自定义数据模型 #

如果你有自定义数据模型(比如结构体或类的数组),你可以通过 ForEach 结合数据模型的属性来生成视图。

例子: #

struct Item {
    var name: String
    var description: String
}

struct ContentView: View {
    let items = [
        Item(name: "Apple", description: "A sweet fruit"),
        Item(name: "Banana", description: "A yellow fruit"),
        Item(name: "Cherry", description: "A small red fruit")
    ]
    
    var body: some View {
        VStack {
            ForEach(items, id: \.name) { item in
                VStack(alignment: .leading) {
                    Text(item.name)
                        .font(.headline)
                    Text(item.description)
                        .font(.subheadline)
                }
                .padding()
            }
        }
    }
}
  • 解释
    • ForEach(items, id: \.name):这里使用了 items 数组中的 Item 类型,id: \.name 表示 Itemname 属性将作为每个视图的唯一标识符。
    • VStack 内部的 Text 显示每个项目的名称和描述。

4. 使用动态数据源 #

ForEach 可以与动态数据源结合使用,特别适用于从网络或数据库加载数据并动态展示。

例子: #

class DataModel: ObservableObject {
    @Published var items = [
        Item(name: "Apple", description: "A sweet fruit"),
        Item(name: "Banana", description: "A yellow fruit")
    ]
}

struct ContentView: View {
    @ObservedObject var dataModel = DataModel()
    
    var body: some View {
        VStack {
            ForEach(dataModel.items, id: \.name) { item in
                VStack(alignment: .leading) {
                    Text(item.name)
                        .font(.headline)
                    Text(item.description)
                        .font(.subheadline)
                }
                .padding()
            }
        }
    }
}
  • 解释
    • 这里使用 @ObservedObject 来观察 DataModel 中的数据,items 会随着数据的更新而自动刷新视图。

5. 可更新的数据 #

如果需要更新数据(例如在 ForEach 中修改数据项),可以通过绑定来实现动态更新。

例子: #

struct ContentView: View {
    @State private var items = ["Apple", "Banana", "Cherry"]
    
    var body: some View {
        VStack {
            ForEach(items, id: \.self) { item in
                Text(item)
                    .padding()
            }
            
            Button("Add Item") {
                items.append("Orange")
            }
        }
    }
}
  • 解释
    • 使用 @State 修饰 items,允许它在视图中发生变化。
    • 按钮的点击会向 items 数组中添加一个新的项,ForEach 会自动刷新视图。

6. 配合 List 使用 #

ForEach 常常与 List 一起使用,特别是在展示动态数据时。List 本身是一个容器,它可以自动处理滚动和视图复用,而 ForEach 用来生成每个列表项。

例子: #

struct ContentView: View {
    let items = ["Apple", "Banana", "Cherry"]
    
    var body: some View {
        List {
            ForEach(items, id: \.self) { item in
                Text(item)
            }
        }
    }
}
  • 解释
    • ForEach 生成每一行的内容,而 List 处理滚动和复用的逻辑。

7. 删除操作与 ForEach #

你可以结合 ForEach 和删除操作来动态更新视图,通常与 List 配合使用。

例子: #

struct ContentView: View {
    @State private var items = ["Apple", "Banana", "Cherry"]
    
    var body: some View {
        List {
            ForEach(items, id: \.self) { item in
                Text(item)
            }
            .onDelete { indexSet in
                items.remove(atOffsets: indexSet)
            }
        }
    }
}
  • 解释
    • 使用 .onDelete 修饰符来添加删除功能,允许用户从列表中删除项。

总结: #

  • ForEach 用于动态生成重复视图,适用于任何容器视图中。
  • 它需要一个数据源(数组、范围等)来循环生成视图,并通过 id 来唯一标识每个视图。
  • ForEachList 配合使用时,可以处理滚动和视图复用,适合展示动态列表。
本文共 1296 字,上次修改于 Feb 4, 2025