SwiftUI ForEach遍历
SwiftUI ForEach遍历

SwiftUI ForEach遍历

ForEach是 SwiftUI 中用来「遍历显示一组视图」的核心工具。

基本用法

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

遍历数字区间 0..<5,每次生成一个 Text 视图。

使用示例

1、遍历数组

let names = ["Alice", "Bob", "Charlie"]

ForEach(names, id: \.self) { name in
    Text("Hello, \(name)")
}

id: \.self 表示每个元素自己就是唯一标识符(前提是元素遵循 Hashable),每个名字都会生成一个 Text。

2、遍历模型数组

struct Person: Identifiable {
    var id = UUID()
    var name: String
}

let people = [
    Person(name: "Tom"),
    Person(name: "Jerry")
]

ForEach(people) { person in
    Text(person.name)
}

3、获取索引遍历

ForEach(Array(people.enumerated()), id: \.element.id) { index, person in
    Text("\(index + 1). \(person.name)")
}

使用enumerated可以获取到对应的数组索引和值,id: \.element.id 使用原对象的 id 作为标识。

在实际应用中可以使用数组索引进行标记或其他用途。

// 可循环的颜色数组
let colorPalette: [Color] = [
    .red, .purple, .blue, .green, .orange, .pink, .yellow, .teal, .mint
]

ForEach(Array(userForeignCurrencies.enumerated()), id:\.1){ index,currency in
    let barColor = colorPalette[index % colorPalette.count]
    VStack {
        Text(symbol)
            .font(.footnote)
            .foregroundColor(Color(hex: "FFFFFF"))
            .fixedSize()
        Rectangle()
            .frame(width: width * ratio * 0.8,height: 8)
            .foregroundColor(barColor)
            .cornerRadius(10)
    }
}

例如,在这段代码中,通过enumerated获取索引后,用于显示不同的背景颜色。

高级用法

1、可变数据绑定

@State private var numbers = [1, 2, 3]

ForEach($numbers, id: \.self) { $number in
    TextField("Number", value: $number, formatter: NumberFormatter())
}

用 $numbers 来绑定每一项,修改某个 TextField 会自动更新 numbers 数组。

2、用于 Core Data

@FetchRequest(sortDescriptors: []) var currencies: FetchedResults<UserForeignCurrency>

ForEach(currencies) { currency in
    Text(currency.symbol ?? "未知")
}

FetchedResults 本身就遵循 RandomAccessCollection 和元素 Identifiable,可以直接用 ForEach。

总结

使用ForEach可以循环遍历数组,前提是元素需要遵循Hashable协议。

ForEach常见的错误是id冲突或不唯一,如果循环遍历的数组没有id,则需要自己添加一个字段或使用 .self。

扩展知识

ForEach的id冲突问题

什么是 id 冲突?

ForEach 的作用是「遍历一个集合,给每个元素生成一个视图」,而 SwiftUI 是声明式 UI,它必须知道每个视图的“身份”,才能决定:

哪些视图要新增;

哪些视图要更新;

哪些视图要删除;

如果多个元素的 id 一样,就会让 SwiftUI 搞不清谁是谁。

冲突示例
struct Item {
    var name: String
}

let items = [Item(name: "A"), Item(name: "A")]

ForEach(items, id: \.name) { item in
    Text(item.name)
}

两个元素 name 都是 “A”,也就是 id 重复了。

SwiftUI 只会认出一个视图,另一个会被忽略或乱刷新,就会遇到刷新不正常、绑定错乱、动画失效等问题。

解决方案

1、让元素遵循 Identifiable,使用唯一 id

struct Item: Identifiable {
    let id = UUID()
    let name: String
}

ForEach(items) { item in
    Text(item.name)
}

这时候 SwiftUI 自动用 item.id 做标识。

2、手动指定唯一的id

如果数据没 id 属性,也可以手动指定,比如用数组的索引:

ForEach(Array(items.enumerated()), id: \.0) { index, item in
    Text("\(index): \(item.name)")
}

enumerated() 生成 (index, item) 的元组,\.0 表示第一个元素,即用 index 作为 id,每一项都有独立的身份。

3、数组元素本身是唯一的

例如有 symbol 这样的货币符号数组:

let currencies = ["USD", "EUR", "JPY"]
ForEach(currencies, id: \.self) { symbol in
    Text(symbol)
}

如果能保证没有重复,这样写是可以的。但如果可能有重复,就危险了!

相关文章

1、SwiftUI ForEach列表排序:https://fangjunyu.com/2025/01/13/swiftui-foreach%e5%88%97%e8%a1%a8%e6%8e%92%e5%ba%8f/

2、Swift 使用ForEach遍历字典:https://fangjunyu.com/2024/10/27/swift-%e4%bd%bf%e7%94%a8foreach%e9%81%8d%e5%8e%86%e5%ad%97%e5%85%b8/

3、SwiftUI的ForEach无法跟踪数组元素问题:https://fangjunyu.com/2024/12/17/swiftui%e7%9a%84foreach%e6%97%a0%e6%b3%95%e8%b7%9f%e8%b8%aa%e6%95%b0%e7%bb%84%e5%85%83%e7%b4%a0%e9%97%ae%e9%a2%98/

4、Swift 使用enumerated()方法获取元素索引:https://fangjunyu.com/2024/10/27/swift-%e4%bd%bf%e7%94%a8enumerated%e6%96%b9%e6%b3%95%e8%8e%b7%e5%8f%96%e5%85%83%e7%b4%a0%e7%b4%a2%e5%bc%95/

如果您认为这篇文章给您带来了帮助,您可以在此通过支付宝或者微信打赏网站开放者。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注