提问人:David Kababyan 提问时间:11/12/2023 最后编辑:David Kababyan 更新时间:11/13/2023 访问量:84
SwiftUI ForEach 抛出索引错误
SwiftUI ForEach Throws out of index error
问:
我似乎不明白为什么这会引发错误? 每个循环的第一个都运行良好,第二个循环抛出错误。 我需要使用数组的索引来获取代码的自定义颜色。否则我会选择普通的 ForEach。
@ViewBuilder
private func chartLegendView() -> some View {
if !viewModel.topTransactions.isEmpty {
VStack {
ForEach(viewModel.topTransactions, id: \.self) { transaction in
Text(transaction.description)
}
ForEach(viewModel.topTransactions.indices, id: \.self) { index in
Text(viewModel.topTransactions[index].description)
}
}
}
}
我已经尝试了所有逻辑选项,但没有任何效果。
更新1:这就是我正在努力实现的目标。 我从 SwiftData 获取一组事务。 我正在从该事务数组(按类别分组和其他一些计算)创建一个自定义对象,并将它们显示在 SwiftUI 图表中。我所拥有的是我的图表图例和图表颜色是自定义的。
我预定义了 10 种颜色,要在数组中的图表上使用。这就是为什么我使用索引从图表中的每个项目中获取颜色的原因。
我的用户可以选择“收入、支出或日期”来筛选来自 SwiftData 的数据并重新生成 UI。我确实同意这个问题看起来像一个模型/获取问题。
这就是我“解决”问题的方式,但它是错误的。当过滤器更换时,我仍然崩溃。
@ViewBuilder
private func chartLegendView() -> some View {
VStack {
ForEach(Array(viewModel.topTransactions.prefix(analyticsTransactionsNumber).enumerated()), id: \.element) { index, transaction in
TopExpenseRow(topExpense: transaction, color: categoryColors[index])
}
}
}
struct TopTransaction: Comparable, Hashable, Identifiable {
let id = UUID()
let total: Double
let percentOfTotal: Double
let category: Category?
var description: String {
"\(name) \(percentOfTotal.noDecimalString)%"
}
var name: String {
category?.name ?? "Unknown"
}
static func < (lhs: TopTransaction, rhs: TopTransaction) -> Bool {
return lhs.total > rhs.total
}
func hash(into hasher: inout Hasher) {
hasher.combine(id)
hasher.combine(name)
hasher.combine(total)
hasher.combine(percentOfTotal)
}
}
答:
-2赞
malhal
11/12/2023
#1
很多错误:
- 删除,这就是什么和是为什么,所以做一个新的自定义。
@ViewBuilder
View
body
ChartLegendView
- Remove ,这会生成代码中不必要的。
if
_ConditionalView
- 删除 ,您需要带有 的真实 id,或者更好地使您的模型结构符合可识别。
id: \.self
ForEach
- 不要在 ForEach 的闭包中使用索引或按索引访问数组。索引在变化中不是唯一的,例如,索引 0 处的交易将获得 ID 0,这与索引 0 处的任何其他交易相同,因此当有多个事务时,它无法检测到索引 0 处的事务被删除,您需要使用真实 ID。
所以它应该看起来像这样:
struct ChartLegendView: View {
let transactions: [Transaction]
var body: View {
ForEach(transactions) { transaction in
Text(transaction.description)
}
}
}
struct Transaction: Identifiable {
var id: String {
return a unique combination of vars, e.g. user + transactionID
}
...
1赞
Yasha
11/12/2023
#2
您的函数看起来不错,问题出在您的数据模型中。如果不研究您的模型结构,我无法假设解决方案,但这里有一个与您的示例相似的示例,该示例适用于有索引和无索引。@ViewBuilder
forEach
struct Thing: Hashable {
var name: String
init(_ name: String) {
self.name = name
}
}
struct Test: View {
@State var data = [Thing("A"), Thing("B"), Thing("C"), Thing("D"), Thing("E"), Thing("F")]
@ViewBuilder
private func chartLegendView() -> some View {
VStack {
ForEach(data, id: \.self) { thing in
Text(thing.name)
}
ForEach(data.indices, id: \.self) { index in
Text(data[index].name)
}
}
}
var body: some View {
chartLegendView()
}
}
除非数据源是静态的,否则最好不要使用索引,因为它包含对集合的强引用。
评论
0赞
lorem ipsum
11/12/2023
索引不安全 “揭开 SwiftUI 的神秘面纱”将此错误归因于
0赞
Yasha
11/13/2023
这是真的,除非数据源是静态的,否则最好不要使用索引。
0赞
lorem ipsum
11/13/2023
在您的例子中,由于数组处于状态,因此它不是静态的
0赞
David Kababyan
11/13/2023
我已经用额外的信息更新了问题。我确实同意这个问题更像是一个模型。我猜最好的选择是将颜色直接添加到我的自定义 TopTransaction 对象并使用普通的 ForEachLoop
0赞
David Kababyan
11/14/2023
谢谢@Yasha的回答。我更新了我最初的问题
评论
index
ForEach