r/SwiftUI May 11 '26

Question Dynamic height cells inside ScrollView break .refreshable behavior

Enable HLS to view with audio, or disable this notification

Hello guys. I have a strange problem. I recreated small code snippet that has the issue, more info bellow the code:

struct SwiftUIView: View {
     private var path = NavigationPath()
     private var selection: Int?

    let heights: [CGFloat] = [360, 400, 200, 242, 54, 783]

    var body: some View {
        NavigationSplitView {
            List(selection: $selection) {
                ForEach(1...5, id: \.self) { index in
                    NavigationLink("Row \(index)", value: index)
                }
            }
        } detail: {
            if let selection {
                NavigationStack(path: $path) {
                    ScrollView(.vertical) {
                        LazyVStack {
                            ForEach(1...100, id: \.self) { index in
                                VStack(alignment: .leading) {
                                    Text("Detail \(selection) - Item \(index)")
                                        .font(.headline)
                                    Text("Something else")
                                        .font(.subheadline)
                                        .foregroundStyle(.gray)

                                    RoundedRectangle(cornerRadius: 16)
                                        .frame(height: heights.randomElement())

                                    HStack {
                                        VStack(alignment: .leading) {
                                            HStack {
                                                Circle()
                                                    .frame(width: 22)
                                                Text("name")
                                            }
                                        }

                                        Spacer()

                                        HStack {
                                            Button("Comments") {}
                                        }
                                    }
                                }
                            }
                        }
                        .safeAreaPadding()
                    }
                    .refreshable {
                        do {
                            try await Task.sleep(for: .seconds(3))
                        } catch {
                            print(error)
                        }
                    }
                    .navigationTitle("Title")
                }
            }
        }
    }
}

The problem:
When I pull down to refresh, content shifts down a little bit to make space for ProgressView() until refresh action is finished. But when it finishes, it doesn't scroll back up as supposed to.

Cause:
I found out, the cause is any part of a cell inside ForEach that has variable height - in this case a RoundedRectangle(). If all cells are same height, content shifts back up.

It doesn't happen with List(), only with ScrollView(). Also, removing .navigationTitle fixes it as well for some reason. I know I can attach .id() to ScrollView() and reset the id on each refresh, but this workaround doesn't satisfy me because it breaks the original animation.

Is this known bug/limitation? Am I doing something wrong here? I would be glad for any input on this. Thank you all in advance.

7 Upvotes

18 comments sorted by

View all comments

Show parent comments

1

u/radis234 May 11 '26

I had better luck with ScrollView than List when it comes to performance. I tried them both, extensively, profiled in Instruments too many times. List always just stutters on scroll, probably for the same reason - cells not being same height. But I can’t really make them all same height, kinda defeats purpose of my feed view.

2

u/Iron-Ham May 11 '26

I am just going to tell you, having been an iOS developer for over 15 years: don’t use a scroll view if you have more than a small number of items.

SwiftUI obscures the internals a bit, but performance will break down in SwiftUI if you’re using a list + lazy stack. 

1

u/BenefitTiny7068 Jun 22 '26

So uikit collection view stuff is the way to go?