LazyVGrid section problem

Hello,

I have problem with generating data from my array.

Here is example code.

import SwiftUI
import Foundation

struct Something:  Identifiable {
	var id=UUID()
	var health: Double
	var age: Int
	var name: String
}

struct ContentView: View {
	
	@State var something:[Something] = [
		Something( health: 100, age: 10, name: "test 1"),
		Something( health: 100, age: 10, name: "test 2"),
		Something( health: 100, age: 10, name: "test 3"),
		Something( health: 100, age: 10, name: "test 4"),
		Something( health: 100, age: 10, name: "test 5"),
		Something( health: 100, age: 10, name: "test 6"),
		Something( health: 100, age: 10, name: "test 7"),
		Something( health: 100, age: 10, name: "test 8"),
		Something( health: 100, age: 10, name: "test 9"),
		Something( health: 100, age: 10, name: "test 10"),
		Something( health: 100, age: 10, name: "test 11"),
		Something( health: 100, age: 10, name: "test 12")
	]
	
	private var columns: [GridItem] = [
		GridItem(.fixed(100), spacing: 16),
		GridItem(.fixed(100), spacing: 16)
	]
	
    var body: some View {
		ScrollView{
			LazyVGrid(columns: columns, spacing: 10,pinnedViews: [.sectionHeaders]){
				ForEach(0 ..< 56) { pole in
					Section(header: Text("Section \(pole)").font(.title)){
						ForEach(0 ..< something.count, id:\.self) { index in
							Text("test nr \(index)")
						}
					}
				}
			}
		}
    }
}

After run this code, I have that problem:

When I replace line code
from: ForEach(0 …< something.count, id:.self) { index in
to: ForEach(0 …< something.count) { index in
almost everything is ok but I got error
Non-constant range: argument must be an integer literal
but sections and data works ok

What is wrong in my example?
Piotr

@mediakon

Welcome to the community.

In the first screen shot you would also have been getting the errors in the console like this:

LazyVGridLayout: the ID 0 is used by multiple child views, this will give undefined results!
LazyVGridLayout: the ID 1 is used by multiple child views, this will give undefined results!
LazyVGridLayout: the ID 2 is used by multiple child views, this will give undefined results!
LazyVGridLayout: the ID 3 is used by multiple child views, this will give undefined results!
LazyVGridLayout: the ID 4 is used by multiple child views, this will give undefined results!
LazyVGridLayout: the ID 5 is used by multiple child views, this will give undefined results!
LazyVGridLayout: the ID 6 is used by multiple child views, this will give undefined results!
LazyVGridLayout: the ID 7 is used by multiple child views, this will give undefined results!
LazyVGridLayout: the ID 8 is used by multiple child views, this will give undefined results!
LazyVGridLayout: the ID 9 is used by multiple child views, this will give undefined results!
LazyVGridLayout: the ID 10 is used by multiple child views, this will give undefined results!
LazyVGridLayout: the ID 11 is used by multiple child views, this will give undefined results!

Replacing the statement as you indicated to ForEach(0..<something.count) { index in results in the error Non-constant range: argument must be an integer literal because when you iterate using 0..<somethiing.count The passed in index needs to be an integer since you are intending to reference an array element using index so it needs to have an id: \.self

The bigger issue is that you are repeating the same array in each Section. A LazyVGrid expects that every element within the entire grid is uniquely identifiable.

@Chris_Parker Thanks for answer. But I still have problem.

Let’s assume purely hypothetically that I have a customer who has repeatable data. The data can only change in, say, every tenth section. What then? How to solve such a problem? I understand that I can add, for example, after Text(“test no. (index)”).id(UUID()) and it somehow works, but I guess it is not a correct notation. However, in some specific cases, can LazyVGrid handle loaded data correctly even if it is repeated?

Below I prepared another example with two arrays and other data. In this case also the data is not displayed correctly.

import SwiftUI
import Foundation

struct Something:  Identifiable {
    var id=UUID()
    var health: Double
    var age: Int
    var name: String
}

struct ContentView: View {
    
    @State var something:[Something] = [
        Something( health: 100, age: 10, name: "test 1"),
        Something( health: 100, age: 10, name: "test 2"),
        Something( health: 100, age: 10, name: "test 3"),
        Something( health: 100, age: 10, name: "test 4"),
        Something( health: 100, age: 10, name: "test 5"),
        Something( health: 100, age: 10, name: "test 6"),
        Something( health: 100, age: 10, name: "test 7"),
        Something( health: 100, age: 10, name: "test 8"),
        Something( health: 100, age: 10, name: "test 9"),
        Something( health: 100, age: 10, name: "test 10"),
        Something( health: 100, age: 10, name: "test 11"),
        Something( health: 100, age: 10, name: "test 12")
    ]
    
    @State var something2:[Something] = [
        Something( health: 11, age: 101, name: "test 11"),
        Something( health: 12, age: 102, name: "test 22"),
        Something( health: 13, age: 103, name: "test 33"),
        Something( health: 14, age: 104, name: "test 44"),
        Something( health: 15, age: 105, name: "test 55"),
        Something( health: 16, age: 106, name: "test 66"),
        Something( health: 177, age: 107, name: "test 77"),
        Something( health: 18, age: 180, name: "test 88"),
        Something( health: 19, age: 109, name: "test 99"),
        Something( health: 20, age: 100, name: "test 100"),
        Something( health: 21, age: 110, name: "test 110"),
        Something( health: 22, age: 111, name: "test 122")
    ]
    
    private var columns: [GridItem] = [
        GridItem(.fixed(100), spacing: 16),
        GridItem(.fixed(100), spacing: 16)
    ]
    
    var body: some View {
        ScrollView{
            LazyVGrid(columns: columns, spacing: 10,pinnedViews: [.sectionHeaders]){
                ForEach(0 ..< 56) { pole in
                    Section(header: Text("Section \(pole)").font(.title)){
                        if (pole % 2 == 0){
                            ForEach(0 ..< something.count, id:\.self) { index in
                                Text("even \(something[index].name)")//.id(UUID())
                            }
                        }else{
                            ForEach(0 ..< something2.count, id:\.self) { index in
                                Text("odd \(something2[index].name)")//.id(UUID())
                            }
                        }
                    }
                }
            }
        }
    }
}

It is possible that I did not understand the operation of LazyVGrid and this is causing my problems…

Going back to your original code, the answer is to do this (which you almost got right in the most recent post).

struct Something { // : Identifiable
//    var id = UUID()
    var health: Double
    var age: Int
    var name: String
}

struct ContentView: View {

    @State var something:[Something] = [
        Something( health: 100, age: 10, name: "test 1"),
        Something( health: 100, age: 10, name: "test 2"),
        Something( health: 100, age: 10, name: "test 3"),
        Something( health: 100, age: 10, name: "test 4"),
        Something( health: 100, age: 10, name: "test 5"),
        Something( health: 100, age: 10, name: "test 6"),
        Something( health: 100, age: 10, name: "test 7"),
        Something( health: 100, age: 10, name: "test 8"),
        Something( health: 100, age: 10, name: "test 9"),
        Something( health: 100, age: 10, name: "test 10"),
        Something( health: 100, age: 10, name: "test 11"),
        Something( health: 100, age: 10, name: "test 12")
    ]

    private var columns: [GridItem] = [
        GridItem(.fixed(100), spacing: 16),
        GridItem(.fixed(100), spacing: 16)
    ]

    var body: some View {
        ScrollView{
            LazyVGrid(columns: columns, spacing: 10,pinnedViews: [.sectionHeaders]){
                ForEach(0..<56, id: \.self) { pole in
                    Section(header: Text("Section \(pole)").font(.title)) {
                        ForEach(0..<something.count, id: \.self) { index in
                            Text("test nr \(index)")
                                .id(UUID())
                        }
                    }
                }
            }
        }
    }
}

By removing the Identifiable conformance from the struct and adding the .id(UUID()) to the text, it works nicely since an identifier exists for each element in the LazyVGrid and it is unique.

As I see it, there is nothing wrong with this approach.

1 Like

@Chris_Parker Thanks for help!

1 Like