SwiftUI Nested ForEach 导致意外排序 [英] SwiftUI Nested ForEach causes unexpected ordering

查看:28
本文介绍了SwiftUI Nested ForEach 导致意外排序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

在我附上的这个 gif 中,当我向上或向下滚动时,Round 后面的第一个变量(下面代码片段中的 ri)有时会改变它的值.然而,这个变量的值在第一个 ForEach 中总是相同的,正如我们可以通过部分名称看到的,显示的回合总是正确的.

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

 列表 {ForEach(Array(self.game.rounds.indices), id: \.self) { ri inSection(header: Text("Round \(ri): \(self.game.rounds[ri])")) {ForEach(Array(self.game.players.indices), id: \.self) { pi in堆栈{文本(\(self.game.players[pi])")文本(回合\(ri),玩家\(pi)")}}}}}

以下是对我造成问题的嵌套 ForEach 的完整视图:

不幸的是,这并没有解决问题.顺序还是乱了.尽管我相信我已经理解了这个问题,但我不知道如何以其他方式解决它.


编辑 3:

我在这里尝试了建议的答案(

解决方案

由于行的非唯一标识符而失败.是用index,但是在不同的section里重复了,所以List行重用缓存引擎混淆了.

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

ForEach(self.game.rounds) { round in//使回合可识别//...ForEach(self.game.players) { player in//使玩家可识别

rounds id 不应与 player id 重叠.

I am using two ForEach() in a nested way, one to display the sections, and the other to display the individual cells of the List view.

My problem is that the iterator variable from the first ForEachis behaving in a very weird way when accessing it in the second ForEach.

In this gif I attached, the first variable behind Round (ri in the code snippet below) changes its value sometimes when I scroll up or down. However the value of this variable is always the same inside the first ForEach, as we can see by the section name where the displayed round is always the correct one.

Here is the code with the nested ForEach statements. However as it is presented here, you'll probably not manage to reproduce the bug. While maybe superstitiously, it seems that the more complex my View is, the more likely the bug is to appear.

        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)")
                        }
                    }
                }
            }
        }

Here is the complete view with the nested ForEachthat causes the problems for me:

https://github.com/charelF/carioca/blob/master/unote/GameView.swift

I'm fairly certain that what I am encountering is actually a bug in SwiftUI. However I'm not sure, and this behaviour may be expected behaviour or some kind of asynchronous loading of cells.


Edit 1:

Either I'm doing something wrong, or it seems that the answer by @Asperi does not really solve the problem. Here's a quick and dirty workaround that I tried to avoid the id's being the same

        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)")
                        }
                    }
                }
            }

The id's are actually never the same, however the rows/cells are still messed up, as seen on this screenshot...

Moreover I'm struggling to find a good way to iterate over these two arrays, given that I need the indices, so I can really iterate over the arrays themselves. The only solutions I can think of is either something ugly like the one above, using .enumerate() (which I tried, and got similar error as above), or maybe transforming my arrays into dictionaries with unique keys...


Edit 2:

I believe I understand the problem. However I have no idea how to solve it. I tried to create custom structs for both Round and Player instead of the Int that I used previously. This means I can write my nested ForEach as follows:

        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)")
                    }
                }
            }
        }

This required rewriting the logic behind Game.swift https://github.com/charelF/carioca/blob/master/unote/Game.swift

Unfortunately this has not solved the problem. The order is still messed up. Even though I believe I have understood the issue, I don't know how to solve it any other way.


Edit 3:

I tried the suggested answer here (Nested ForEach (and List) in Views give unexpected results) (enclosing the view inside the outer ForEach with a group), however the error still persists


Edit 4:

I finally got it to work, unfortunately my solution is more of a hack and really not very clean. The solution is indeed as in the answer, all the cells need unique identifiers.

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)")
                    
                }
            }
        }
    }
}

As seen in the screenshot below, the 4 cells below round 7 have different identifiers than the 4 cells below round 8, which, if I understand correctly, solves my problem.

Unfortunately the solution required some, imo, ugly workarounds in my game logic. https://github.com/charelF/carioca/blob/master/unote/Game.swift

解决方案

It fails due to non-unique identifiers for rows. It is used index, but it is repeated in different sections, so List row reuse caching engine confused.

In the described scenario it should be selected something unique for items that are shown in rows, like (scratchy)

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

and rounds ids should not overlap with player ids as well.

这篇关于SwiftUI Nested ForEach 导致意外排序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆