0

I experience kind of weird behavior of placing Text view in nested ForEach in one LazyVGrid view. My code look like this

LazyVGrid(columns: dataColumns) {
    ForEach(months, id: \.self) { month in
        Text("\(dateFormatter.string(from: month))")
    }
    ForEach(categories) { category in
        ForEach(months, id: \.self) { month in
            Text("aaa")
        }
    }
}

and "aaa" string is not shown but if I add just another Text view like this

ForEach(months, id: \.self) { month in
    Text("aaa")
    Text("bbb")
}

then both Text views with strings are repeatedly shown as expected. Any idea? Thanks.

Dawy
  • 770
  • 6
  • 23

1 Answers1

2

The issue here is to do with view identity. LazyVGrid uses each views identity to know when they need to be loaded in.

Structural identity is also used by SwiftUI to identify views based on the structure of their view hierarchy. This is why when you added another view to the ForEach instance the views show, as they now are uniquely structurally identifiable from the other ForEach content.

Here, you have two separate ForEach instances inside the LazyVGrid which use the same identifiers. Two of the forEach instances are using months with the id \.self to identify them. These are conflicting and should be unique to avoid these sorts of issues.

To fix this you should preferably use a unique array of elements for each ForEach instance, though could create a copy of the months array to use in the second ForEach instead of referring to the same instances. Here's an example:

import SwiftUI
import MapKit

struct MyType: Identifiable, Hashable {
    var id = UUID()

    var name = "Tester"
}

struct ContentView: View {
    let columns = [GridItem(.flexible()), GridItem(.flexible())]
    var months: [MyType] = []
    var monthsCopy: [MyType] = []

    private var monthValues: [MyType] {
        [MyType()]
    }

    init() {
        months = monthValues
        monthsCopy = monthValues
    }

    var body: some View {
        LazyVGrid(columns: columns) {
            ForEach(months) { _ in
                Text("Test1")
                Text("Test2")
            }

            ForEach(monthsCopy) { _ in
                Text("Test3")
                Text("Test4")
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

If you replace ForEach(monthsCopy) with ForEach(months) you will see the issue you were faced with.

whiteio
  • 161
  • 5