SwiftUI 9.ForEach介绍和使用

SwiftUI 的 ForEach 是一个用于动态生成视图的结构,它根据集合数据循环渲染子视图。与 ListPicker 不同,ForEach 本身不提供容器布局,而是依赖父容器(如 ListHStackVStack)来决定显示方式。以下是其详细介绍及使用方法:


一、基本用法

1. 遍历简单集合

直接遍历数组并生成视图:

struct ContentView: View {
    let fruits = ["苹果", "香蕉", "橙子"]
    
    var body: some View {
        VStack {
            ForEach(fruits, id: \.self) { fruit in
                Text(fruit)
            }
        }
    }
}
  • id: \.self:要求集合元素是唯一且可哈希的(如 StringInt)。
  • 若数据元素不唯一,会导致运行时错误。
2. 遍历 Range

生成固定数量的视图:

ForEach(0..<5) { index in
    Text("第 \(index) 项")
}

二、处理复杂数据

1. 遵循 Identifiable 协议

为自定义类型添加唯一标识:

struct Fruit: Identifiable {
    let id = UUID()  // 唯一标识
    var name: String
}

struct ContentView: View {
    let fruits = [Fruit(name: "苹果"), Fruit(name: "香蕉")]
    
    var body: some View {
        List {
            ForEach(fruits) { fruit in
                Text(fruit.name)
            }
        }
    }
}
  • 无需显式指定 id,因为 Identifiable 类型已提供 id 属性。
2. 手动指定唯一标识

若数据未遵循 Identifiable,需手动指定 id

ForEach(fruits, id: \.name) { fruit in
    Text(fruit.name)
}
  • 此处假设 name 是唯一的。

三、与容器结合使用

1. List 中动态生成行
List {
    ForEach(fruits) { fruit in
        Text(fruit.name)
    }
}
2. HStack/VStack 中布局

生成水平或垂直排列的视图:

HStack {
    ForEach(1...3, id: \.self) { number in
        Text("\(number)")
            .padding()
            .background(Color.blue)
            .cornerRadius(5)
    }
}

四、高级用法

1. 条件渲染

结合 if 语句动态控制子视图:

ForEach(fruits) { fruit in
    if fruit.isAvailable {
        Text(fruit.name)
    } else {
        Text("缺货: \(fruit.name)")
            .foregroundColor(.gray)
    }
}
2. 动态过滤数据

使用 filter 预处理数据:

ForEach(fruits.filter { $0.isAvailable }) { fruit in
    Text(fruit.name)
}
3. 结合 Binding 数据

在可编辑列表中动态更新数据:

struct ContentView: View {
    @State private var fruits = [Fruit(name: "苹果"), Fruit(name: "香蕉")]
    
    var body: some View {
        List {
            ForEach($fruits) { $fruit in
                TextField("名称", text: $fruit.name)
            }
        }
    }
}

五、与 List 的区别

特性ForEachList
布局容器无,依赖父容器(如 VStack自带滚动容器和默认样式
性能优化无自动优化,依赖父容器实现默认懒加载,仅渲染可见项
交互功能无内置操作(如滑动删除)支持滑动删除、移动、分组等
动态数据绑定需手动处理数据变化支持 @State@ObservedObject

六、常见问题及解决

1. 错误:ID 不唯一
  • 现象ForEach 报错 Identifiable 冲突。
  • 解决
    • 确保数据源唯一性。
    • 使用 id: \.self 或自定义唯一标识。
2. 动态数据不更新
  • 现象:数据变化后视图未刷新。
  • 解决
    • 使用 @State@ObservedObject 包装数据。
    • 确保数据遵循 Identifiable

七、完整示例

struct Fruit: Identifiable {
    let id = UUID()
    var name: String
    var isAvailable: Bool
}

struct ContentView: View {
    @State private var fruits = [
        Fruit(name: "苹果", isAvailable: true),
        Fruit(name: "香蕉", isAvailable: false),
        Fruit(name: "橙子", isAvailable: true)
    ]
    
    var body: some View {
        NavigationStack {
            List {
                Section(header: Text("水果列表")) {
                    ForEach($fruits) { $fruit in
                        HStack {
                            Text(fruit.name)
                            Spacer()
                            Toggle("有货", isOn: $fruit.isAvailable)
                        }
                    }
                }
                
                Section(header: Text("统计")) {
                    Text("有货商品:\(fruits.filter { $0.isAvailable }.count) 个")
                }
            }
            .navigationTitle("库存管理")
        }
    }
}

八、关键总结

  • 核心作用:根据集合数据动态生成视图。
  • 唯一标识:必须确保每个元素有唯一的 id
  • 灵活组合:可与任何布局容器(如 ListHStack)结合使用。
  • 性能注意:在超长列表中,优先使用 ListLazyVStack 优化性能。

通过 ForEach,你可以高效处理动态数据渲染,结合 SwiftUI 的声明式语法,快速构建复杂的交互界面。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

长沙火山

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值