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/