SwiftUI嵌套ForEach导致意外排序

时间:2020-07-16 08:32:11

标签: swift swiftui-list swiftui

我正在以嵌套方式使用两个ForEach(),一个用于显示部分,另一个用于显示“列表”视图的各个单元格。

我的问题是,在第二个ForEach中访问它时,第一个ForEach中的迭代器变量表现得很怪异。

enter image description here

在我所附的这个gif文件中,Round(下面的代码片段中的{ri)后面的第一个变量有时会在我向上或向下滚动时更改其值。但是,此变量的值在第一个ForEach中始终是相同的,正如我们从节名可以看到的那样,其中显示的回合始终是正确的回合。

这是带有嵌套ForEach语句的代码。但是,如此处所示,您可能无法重现该错误。尽管可能有些迷信,但我的View越复杂,该错误出现的可能性就越大。

        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实际上从不相同,但是行/单元仍然被弄乱了,如此屏幕截图所示...

此外,鉴于我需要索引,我正在努力找到一种遍历这两个数组的好方法,因此我可以真正遍历数组本身。我能想到的唯一解决方案是使用.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:

我终于可以使用它了,不幸的是,我的解决方案更像是破解,实际上不是很干净。解决方案的确如答案中所述,所有单元都需要唯一的标识符。

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

1 个答案:

答案 0 :(得分:6)

由于行的标识符不唯一而失败。它使用索引,但在不同的部分重复使用,因此列表行重用缓存引擎感到困惑。

在所描述的情况下,应该为行中显示的项目选择唯一的内容,例如(抓痒的)

ForEach(self.game.rounds) { round in  // make round identifiable
      // ...
        ForEach(self.game.players) { player in // make player identifiable

rounds标识也不应与player标识重叠。