SwiftUI Nested ForEach 导致意外排序

Posted

技术标签:

【中文标题】SwiftUI Nested ForEach 导致意外排序【英文标题】:SwiftUI Nested ForEach causes unexpected ordering 【发布时间】:2020-07-16 08:32:11 【问题描述】:

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

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

在我附加的这个 gif 中,Round 后面的第一个变量(下面的代码 sn-p 中的ri)有时会在我向上或向下滚动时更改其值。然而,这个变量的值在第一个ForEach 中始终是相同的,正如我们从部分名称中看到的那样,显示的轮次始终是正确的轮次。

这是带有嵌套ForEach 语句的代码。但是,正如这里介绍的那样,您可能无法重现该错误。虽然可能有点迷信,但似乎我的视图越复杂,错误出现的可能性就越大。

        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.swifthttps://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】:

由于行的非唯一标识符而失败。使用索引,但在不同的部分重复,所以List行重用缓存引擎混淆了。

在所描述的场景中,应该为在行中显示的项目选择独特的东西,例如(scratchy)

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

并且rounds ids 也不应该与player ids 重叠。

【讨论】:

这实际上是有道理的。因此,如果我理解正确,因为我的标识符本身就是整数,这意味着回合 id 可能与玩家 id 相同,从而导致问题。我会试试这个,但这听起来很有希望,很可能是我的问题的原因。 我尝试了你的建议,遗憾的是没有成功。我用更多细节编辑了我的问题。我可能误解了您的回答,我不确定我是否做了正确的更改... @charel-f,你做的几乎一样。这些行不是由您看到的文本(“ri 14, pi 0”,看起来很独特)标识的,而是由players.indices), id: \.self 这个id 标识的,所以第 0 部分有行:0、1、2.. ,第 1 部分的第 0、1、2 行...对于列表,这些行被标识为相等,并且出于性能原因可能不会更新/刷新。 哦,再说一次,如果我这次理解正确,我内心的ForEach 需要一个给定玩家+回合组合唯一的 id,否则玩家行会在回合之间混淆(即我正在观察的行为)。我会试试这个,谢谢

以上是关于SwiftUI Nested ForEach 导致意外排序的主要内容,如果未能解决你的问题,请参考以下文章

视图模型数组上的 ForEach 循环问题导致 Xcode 编译错误 - SwiftUI

SwiftUI:VStack 中 ForEach 中的 HStack 使多行文本重叠

SwiftUI Nested GeometryReader - 破坏 UI

SwiftUI - 形状导致反向数组访问

SwiftUI NavigationView 与 List 内 ForEach 中的 NavigationLink 来回跳转

如何在溢出屏幕的 SwiftUI 中制作无限宽度的视图?