SwiftUI Nested ForEach 导致意外排序
SwiftUI Nested ForEach causes unexpected ordering
我正在以嵌套方式使用两个 ForEach()
,一个用于显示部分,另一个用于显示列表视图的各个单元格。
我的问题是第一个 ForEach
的迭代器变量在第二个 ForEach
.
中访问它时表现得很奇怪
在我附上的这个 gif 中,当我向上或向下滚动时,Round 后面的第一个变量(下面代码片段中的 ri
)有时会改变它的值。然而,这个变量的值在第一个 ForEach
中始终相同,正如我们可以从显示的回合始终正确的部分名称中看到的那样。
这是带有嵌套 ForEach
语句的代码。然而,如此处所示,您可能无法重现该错误。虽然可能有点迷信,但似乎我的 View 越复杂,出现 bug 的可能性就越大。
List {
ForEach(Array(self.game.rounds.indices), id: \.self) { ri in
Section(header: Text("Round \(ri): \(self.game.rounds[ri])")) {
ForEach(Array(self.game.players.indices), id: \.self) { pi in
HStack {
Text("\(self.game.players[pi])")
Text("Round \(ri), Player \(pi)")
}
}
}
}
}
这是完整的视图,其中嵌套 ForEach
给我带来了问题:
https://github.com/charelF/carioca/blob/master/unote/GameView.swift
我相当确定我遇到的实际上是 SwiftUI 中的错误。但是我不确定,这种行为可能是预期行为或某种异步加载单元格。
编辑 1:
要么我做错了什么,要么@Asperi 的回答似乎并没有真正解决问题。这是我试图避免 id 相同的快速而肮脏的解决方法
List {
ForEach([10,11,12,13,14,15,16,17,18,19] /*Array(self.game.rounds.enumerated())*/, id: \.self) { ri in
Section(header: Text("Round \(ri-10): \(self.game.rounds[ri-10])")) {
ForEach(Array(self.game.players.indices), id: \.self) { pi in
HStack {
Text("ri \(ri), pi \(pi)")
}
}
}
}
ID 实际上从不相同,但是 rows/cells 仍然是乱七八糟的,如屏幕截图所示...
此外,考虑到我需要索引,我正在努力寻找一种迭代这两个数组的好方法,这样我才能真正迭代数组本身。我能想到的唯一解决方案要么像上面那样丑陋,使用 .enumerate()
(我试过,并得到与上面类似的错误),要么将我的数组转换为具有唯一键的字典...
编辑 2:
我相信我理解了这个问题。但是我不知道如何解决它。我尝试为 Round
和 Player
创建自定义结构,而不是之前使用的 Int
。这意味着我可以如下编写嵌套 ForEach
:
List {
ForEach(self.game.rounds, id: \.id) { round in
Section(header: Text("\(round.number): \(round.desc)")) {
ForEach(self.game.players, id: \.id) { player in
Text("\(player.name), \(round.number)")
}
}
}
}
这需要重写 Game.swift
https://github.com/charelF/carioca/blob/master/unote/Game.swift
背后的逻辑
很遗憾,这并没有解决问题。秩序依旧混乱。尽管我相信我已经理解了这个问题,但我不知道如何用其他方式解决它。
编辑 3:
我在这里尝试了建议的答案 (Nested ForEach (and List) in Views give unexpected results)(将视图包含在外部 ForEach
中),但是错误仍然存在
编辑 4:
我终于让它工作了,不幸的是我的解决方案更像是一个 hack,而且真的不是很干净。解决方案确实和答案一样,所有的单元格都需要唯一的标识符。
List {
ForEach(self.game.rounds) { round in
Group {
Section(header: Text("\(round.number): \(round.desc)")) {
ForEach(self.game.scoreBoard[round]!, id: \.self.id) { sbe in
Text("\(round.number) \(sbe.id)")
}
}
}
}
}
如下面的屏幕截图所示,第 7 轮下方的 4 个单元格与第 8 轮下方的 4 个单元格具有不同的标识符,如果我理解正确,这将解决我的问题。
不幸的是,该解决方案在我的游戏逻辑中需要一些 imo 丑陋的解决方法。 https://github.com/charelF/carioca/blob/master/unote/Game.swift
由于行的非唯一标识符而失败。用的是索引,但是在不同的section中是重复的,所以List row reuse caching engine 混淆了。
在所描述的场景中,应该为行中显示的项目选择一些独特的东西,例如(刮擦)
ForEach(self.game.rounds) { round in // make round identifiable
// ...
ForEach(self.game.players) { player in // make player identifiable
和 rounds
ID 也不应与 player
ID 重叠。
我正在以嵌套方式使用两个 ForEach()
,一个用于显示部分,另一个用于显示列表视图的各个单元格。
我的问题是第一个 ForEach
的迭代器变量在第二个 ForEach
.
在我附上的这个 gif 中,当我向上或向下滚动时,Round 后面的第一个变量(下面代码片段中的 ri
)有时会改变它的值。然而,这个变量的值在第一个 ForEach
中始终相同,正如我们可以从显示的回合始终正确的部分名称中看到的那样。
这是带有嵌套 ForEach
语句的代码。然而,如此处所示,您可能无法重现该错误。虽然可能有点迷信,但似乎我的 View 越复杂,出现 bug 的可能性就越大。
List {
ForEach(Array(self.game.rounds.indices), id: \.self) { ri in
Section(header: Text("Round \(ri): \(self.game.rounds[ri])")) {
ForEach(Array(self.game.players.indices), id: \.self) { pi in
HStack {
Text("\(self.game.players[pi])")
Text("Round \(ri), Player \(pi)")
}
}
}
}
}
这是完整的视图,其中嵌套 ForEach
给我带来了问题:
https://github.com/charelF/carioca/blob/master/unote/GameView.swift
我相当确定我遇到的实际上是 SwiftUI 中的错误。但是我不确定,这种行为可能是预期行为或某种异步加载单元格。
编辑 1:
要么我做错了什么,要么@Asperi 的回答似乎并没有真正解决问题。这是我试图避免 id 相同的快速而肮脏的解决方法
List {
ForEach([10,11,12,13,14,15,16,17,18,19] /*Array(self.game.rounds.enumerated())*/, id: \.self) { ri in
Section(header: Text("Round \(ri-10): \(self.game.rounds[ri-10])")) {
ForEach(Array(self.game.players.indices), id: \.self) { pi in
HStack {
Text("ri \(ri), pi \(pi)")
}
}
}
}
ID 实际上从不相同,但是 rows/cells 仍然是乱七八糟的,如屏幕截图所示...
此外,考虑到我需要索引,我正在努力寻找一种迭代这两个数组的好方法,这样我才能真正迭代数组本身。我能想到的唯一解决方案要么像上面那样丑陋,使用 .enumerate()
(我试过,并得到与上面类似的错误),要么将我的数组转换为具有唯一键的字典...
编辑 2:
我相信我理解了这个问题。但是我不知道如何解决它。我尝试为 Round
和 Player
创建自定义结构,而不是之前使用的 Int
。这意味着我可以如下编写嵌套 ForEach
:
List {
ForEach(self.game.rounds, id: \.id) { round in
Section(header: Text("\(round.number): \(round.desc)")) {
ForEach(self.game.players, id: \.id) { player in
Text("\(player.name), \(round.number)")
}
}
}
}
这需要重写 Game.swift
https://github.com/charelF/carioca/blob/master/unote/Game.swift
很遗憾,这并没有解决问题。秩序依旧混乱。尽管我相信我已经理解了这个问题,但我不知道如何用其他方式解决它。
编辑 3:
我在这里尝试了建议的答案 (Nested ForEach (and List) in Views give unexpected results)(将视图包含在外部 ForEach
中),但是错误仍然存在
编辑 4:
我终于让它工作了,不幸的是我的解决方案更像是一个 hack,而且真的不是很干净。解决方案确实和答案一样,所有的单元格都需要唯一的标识符。
List {
ForEach(self.game.rounds) { round in
Group {
Section(header: Text("\(round.number): \(round.desc)")) {
ForEach(self.game.scoreBoard[round]!, id: \.self.id) { sbe in
Text("\(round.number) \(sbe.id)")
}
}
}
}
}
如下面的屏幕截图所示,第 7 轮下方的 4 个单元格与第 8 轮下方的 4 个单元格具有不同的标识符,如果我理解正确,这将解决我的问题。
不幸的是,该解决方案在我的游戏逻辑中需要一些 imo 丑陋的解决方法。 https://github.com/charelF/carioca/blob/master/unote/Game.swift
由于行的非唯一标识符而失败。用的是索引,但是在不同的section中是重复的,所以List row reuse caching engine 混淆了。
在所描述的场景中,应该为行中显示的项目选择一些独特的东西,例如(刮擦)
ForEach(self.game.rounds) { round in // make round identifiable
// ...
ForEach(self.game.players) { player in // make player identifiable
和 rounds
ID 也不应与 player
ID 重叠。