本文介绍了视图不会在嵌套 ForEach 循环中重新呈现的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下组件可以呈现半透明字符网格:

I have the following component that renders a grid of semi transparent characters:

    var body: some View {
        VStack{
            Text("\(self.settings.numRows) x \(self.settings.numColumns)")
            ForEach(0..<self.settings.numRows){ i in
                Spacer()
                    HStack{
                        ForEach(0..<self.settings.numColumns){ j in
                            Spacer()
                            // why do I get an error when I try to multiply i * j
                            self.getSymbol(index:j)
                            Spacer()
                        }
                    }
                Spacer()
            }
        }
    }

settings 是一个 EnvironmentObject

settings is an EnvironmentObject

每当 settings 更新时,最外面的 VStack 中的 Text 都会正确更新.但是,视图的其余部分不会更新(网格具有与以前相同的尺寸).这是为什么?

Whenever settings is updated the Text in the outermost VStack is correctly updated. However, the rest of the view is not updated (Grid has same dimenstions as before). Why is this?

第二个问题:为什么不能访问内部 ForEach 循环中的 i 并将其作为参数传递给函数?

Second question:Why is it not possible to access the i in the inner ForEach-loop and pass it as a argument to the function?

我在外部 ForEach 循环中遇到错误:

I get an error at the outer ForEach-loop:

无法推断通用参数数据"

推荐答案

TL;DR

您的 ForEach 需要在您的范围后添加 id: \.self.

ForEach 有几个初始值设定项.您正在使用

ForEach has several initializers. You are using

init(_ data: Range<Int>, @ViewBuilder content: @escaping (Int) -> Content)

其中 data 必须 是一个常量.

where data must be a constant.

如果您的范围可能会发生变化(例如,您正在从数组中添加或删除项目,这将更改上限),那么您需要使用

If your range may change (e.g. you are adding or removing items from an array, which will change the upper bound), then you need to use

init(_ data: Data, id: KeyPath<Data.Element, ID>, content: @escaping (Data.Element) -> Content)

您为 id 参数提供了一个键路径,它唯一地标识了 ForEach 循环遍历的每个元素.在 Range 的情况下,您循环的元素是一个 Int 指定数组索引,它是唯一的.因此,您可以简单地使用 \.self 键路径让 ForEach 通过其自己的值识别每个索引元素.

You supply a keypath to the id parameter, which uniquely identifies each element that ForEach loops over. In the case of a Range<Int>, the element you are looping over is an Int specifying the array index, which is unique. Therefore you can simply use the \.self keypath to have the ForEach identify each index element by its own value.

实际操作如下:

struct ContentView: View {
    @State var array = [1, 2, 3]

    var body: some View {
        VStack {
            Button("Add") {
                self.array.append(self.array.last! + 1)
            }

            // this is the key part  v--------v
            ForEach(0..<array.count, id: \.self) { index in
                Text("\(index): \(self.array[index])")
                //Note: If you want more than one views here, you need a VStack or some container, or will throw errors
            }
        }
    }
}

如果你运行它,你会看到当你按下按钮将项添加到数组时,它们将自动出现在 VStack 中.如果您删除id: \.self",您将看到原始错误:

If you run that, you'll see that as you press the button to add items to the array, they will appear in the VStack automatically. If you remove "id: \.self", you'll see your original error:

`ForEach(_:content:)` should only be used for *constant* data.
Instead conform data to `Identifiable` or use `ForEach(_:id:content:)`
and provide an explicit `id`!"

这篇关于视图不会在嵌套 ForEach 循环中重新呈现的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-18 08:48