swiftswiftuiswiftui-list

SwiftUI List Rendering More Cells than Necessary When Using a Custom Cell View


I am experimenting with SwiftUI and noticed unexpected behavior when using a custom view inside a List. Here’s the simplified code:

import SwiftUI

struct ContentView: View {
    var body: some View {
        NavigationView {
            List(0..<1000, id: \.self) { index in
                let _ = print(index)
                Text("\(index)")        // On initial load ~18 cells are created
//                Cell(index: index)    // On initial load 1000 cells are created
            }
        }
    }
}

struct Cell: View {
    let index: Int
    var body: some View {
        Text("\(index)")
    }
}

#Preview {
    ContentView()
}

Observed Behavior:

When using Text("\(index)") directly, only around 18 cells are created on the initial load (as expected due to the lazy rendering of SwiftUI's List).
When switching to the custom view Cell(index: index), all 1000 cells are created immediately, significantly affecting performance.

Question:

Why does using a custom view inside the List cause all cells to be rendered eagerly, and how can I fix this while still using the custom view?


Solution

  • Note, NavigationView is deprecated use NavigationStack instead.

    List are lazy by default, but your side effect let _ = print(item) is responsible for showing all the index, it is eagerly evaluated. Remove it to ...fix this while still using the custom view?

    To see this in action, remove let _ = print(item) from the loop, and instead add

    .onAppear { print(index) } 
    

    to your Cell, such as

    struct Cell: View {
        let index: Int
        var body: some View {
            Text("\(index)")
                .onAppear {
                    print(index)  // <-- here
                }
        }
    }
    

    The eager evaluation (of print) does not happens when Text is used, because Text is a built-in view in SwiftUI that has knowledge and optimizations for handling primitive views. The eager evaluation is not done because it is not a custom view.