List sections empty after adding .id()

I have a section for each element in array. I need to use ScrollViewReader so I need to add .id() to each row. However, when I add id, list rows become empty. It works fine without id modifier. See the image below:

Here's the sample project I have made that demonstrates this problem:

import SwiftUI
import PlaygroundSupport

struct Interval: Identifiable {
    var id = UUID()
    var index: Int
    var name : String {
        "Interval \(index)"
    }
}

struct ContentView: View {
    
    var intervals: [Interval] = (1...9).map { index in Interval(index: index) }
    
    var body: some View {
        List {
            ForEach(intervals, id: \.id) { interval in
                Section {
                    Text(interval.name)
                        //.id(interval.id) // try removing this comment
                }
            }
        }
    }
    
}

PlaygroundPage.current.setLiveView(ContentView())

Some debugging that I have done:

Having only 1 section works, but I need one section for every element.

Section {
    ForEach(intervals, id: \.id) { interval in
        Text(interval.name)
            .id(interval.id)
    }
}

Adding each section manually also works, but this can't be done as I have lots of section in my actual project.

Section {
    Text(intervals[0].name)
        .id(intervals[0].id)
}
Section {
    Text(intervals[1].name)
        .id(intervals[1].id)
}

Does anyone know how to fix this? Thanks.

Accepted Reply

Although @snuff4's answer solved the empty cells issue in my real project, scrolling to desired cell/row wasn't working when using ScrollViewReader's scrollTo method.

I have come up with a weird fix that works for me. It is weird because I don't know how and why it works, but it works. If anyone can explain why it works, I'll be very thankful.

The solution is, I created a new ViewModifier named EmbedInSection that just embeds the content in Section.

struct EmbedInSection: ViewModifier {
    func body(content: Content) -> some View {
        Section {
            content
        }
    }
}

And I used it like:

var body: some View {
    List {
        ForEach(intervals) { interval in
            Text(interval.name)
                .id(interval.id)
                .modifier(EmbedInSection())
        }
    }
}

Replies

Try wrapping the view inside ForEach with a VStack:

     List {
            ForEach(intervals, id: \.id) { interval in
                Section {
                    VStack { // <-- HERE
                        Text(interval.name)
                     }
                     .id(interval.id) 
                }
            }
        }
  • Thanks for the answer! Although this did work perfectly in the sample project I made, in my real project it fixed the empty cells but broke the ScrollViewReader's scrollTo method. It wasn't scrolling to the desired row. I have come with a weird fix that works fine without adding any extra VStack. I don't know how and why that fix works. But I will add a separate answer with that fix to my question.

Add a Comment

Although @snuff4's answer solved the empty cells issue in my real project, scrolling to desired cell/row wasn't working when using ScrollViewReader's scrollTo method.

I have come up with a weird fix that works for me. It is weird because I don't know how and why it works, but it works. If anyone can explain why it works, I'll be very thankful.

The solution is, I created a new ViewModifier named EmbedInSection that just embeds the content in Section.

struct EmbedInSection: ViewModifier {
    func body(content: Content) -> some View {
        Section {
            content
        }
    }
}

And I used it like:

var body: some View {
    List {
        ForEach(intervals) { interval in
            Text(interval.name)
                .id(interval.id)
                .modifier(EmbedInSection())
        }
    }
}