View is not rerendered in Nested ForEach loop

前端 未结 2 1296
孤独总比滥情好
孤独总比滥情好 2020-12-18 20:28

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

    var body: some View {
               


        
2条回答
  •  無奈伤痛
    2020-12-18 20:46

    TL;DR

    Your ForEach needs id: \.self added after your range.

    Explanation

    ForEach has several initializers. You are using

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

    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, content: @escaping (Data.Element) -> Content)
    

    You supply a keypath to the id parameter, which uniquely identifies each element that ForEach loops over. In the case of a Range, 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.

    Here is what it looks like in practice:

    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..

    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`!"
    

提交回复
热议问题