How we can Lazy load data to a Form/List in SwiftUI?

Viewed 1754

I am generating Text in ForEach loop and using a Form to give a style, how ever it does not work in heavy data, I am trying to use LazyVStack in sow how but I was not successful! How we can load data to a Form with heavy data?

PS: the same code just working fine with ForEach and without using Form or List, even with heavier data with help of LazyVStack. I just want make codes work with using Form or List.

    struct ContentView: View
{
    
    var body: some View
    {
    Form
    {
        ForEach(0 ..< 1000000) { index in
            Text("Row \(index)")
        }

    }

    
    }

}
2 Answers

The problem is that static data source (and all internal dependencies) are allocated at once, even if later the corresponding UI rows will be shown only when needed.

Here is possible approach to solve this scenario - to make data container dynamic and load data by blocks (taking into account that List/Form reuses rows)

Tested with Xcode 12 / iOS 14

struct ContentView: View {
    let max = 1000000 // even Int.max, if you want
    let block = 10

    @State private var indices = [0]   // << initially loaded small part

    var body: some View {
        List {
            ForEach(indices, id: \.self) { index in
                Text("Row \(index)")
                    .onAppear {

                        // load next block when last row shown (or can be 
                        // tuned to preload in advance)

                        if indices.last == index && index < max {
                            let next = max - index
                            indices.append(contentsOf:
                                Array(index + 1..<index+(next > block ? block : next)))
                        }
                    }
            }
        }
    }
}

The code you are looking for is this:

struct ContentView: View {
    var body: some View
    {
        Form
        {
            ForEach(0 ..< 100000, id: \.self) { index in
                Text("Row \(index)")
            }
        }
    }
}

You should note two things:

First, you should give your ForEach an id to use as it does not necessarily know what item from another. This is important for reordering and such.

Second, I dropped an order of magnitude off your ForEach. You were asking for a Form with 1 million rows. That is just more than is reasonable for the rendering engine. No one is going to scroll through 1 million rows. It works fine at one hundred-thousand. And the same thing goes, no one is going to scroll through one hundred-thousand items.

The other thing you haven't given is what your data backing is. Where is your data coming from? You should limit the amount of data displayed at any one time, but that depends on the source. For example, if you were using Core Data, you could limit the objects coming from your store. You shouldn't be displaying more data than a user could ever possibly use. If you are, you have not thought through what you are trying to accomplish.

Related