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:

我相信我理解了这个问题。但是我不知道如何解决它。我尝试为 RoundPlayer 创建自定义结构,而不是之前使用的 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 重叠。