ScrollView + LazyVStack + dynamic height views cause scroll glitches on iOS 26

I’m seeing unexpected scroll behavior when embedding a LazyVStack with dynamically sized views inside a ScrollView. Everything works fine when the item height is fixed (e.g. colored squares), but when I switch to text views with variable height, the scroll position jumps and glitches—especially when the keyboard appears or disappears. This only happens on iOS 26, it works fine on iOS 18.

Working version

struct Model: Identifiable {
    let id = UUID()
}

struct ModernScrollView: View {
    
    @State private var models: [Model] = []
    @State private var scrollPositionID: String?
    @State private var text: String = ""
    @FocusState private var isFocused
    
    // MARK: - View
    
    var body: some View {
        scrollView
            .safeAreaInset(edge: .bottom) { controls }
            .task { reset() }
    }
    
    // MARK: - Subviews
    
    private var scrollView: some View {
        ScrollView {
            LazyVStack {
                ForEach(models) { model in
                    SquareView(color: Color(from: model.id))
                        .id(model.id.uuidString)
                }
            }
            .scrollTargetLayout()
        }
        .scrollPosition(id: $scrollPositionID)
        .scrollDismissesKeyboard(.interactively)
        .defaultScrollAnchor(.bottom)
        .onTapGesture {
            isFocused = false
        }
    }
    
    private var controls: some View {
        VStack {
            HStack {
                Button("Add to top") {
                    models.insert(contentsOf: makeModels(3), at: 0)
                }
                Button("Add to bottom") {
                    models.append(contentsOf: makeModels(3))
                }
                Button("Reset") {
                    reset()
                }
            }
            HStack {
                Button {
                    scrollPositionID = models.first?.id.uuidString
                } label: {
                    Image(systemName: "arrow.up")
                }
                Button {
                    scrollPositionID = models.last?.id.uuidString
                } label: {
                    Image(systemName: "arrow.down")
                }
            }
            TextField("Input", text: $text)
                .padding()
                .background(.ultraThinMaterial, in: .capsule)
                .focused($isFocused)
                .padding(.horizontal)
        }
        .padding(.vertical)
        .buttonStyle(.bordered)
        .background(.regularMaterial)
    }
    
    // MARK: - Private
    
    private func makeModels(_ count: Int) -> [Model] {
        (0..<count).map { _ in Model() }
    }
    
    private func reset() {
        models = makeModels(3)
    }
    
}

// MARK: - Color+UUID

private extension Color {
    init(from uuid: UUID) {
        let hash = uuid.uuidString.hashValue
        let r = Double((hash & 0xFF0000) >> 16) / 255.0
        let g = Double((hash & 0x00FF00) >> 8) / 255.0
        let b = Double(hash & 0x0000FF) / 255.0
        self.init(red: abs(r), green: abs(g), blue: abs(b))
    }
}

Not working version

When I replace the square view with a text view that generates random multiline text:

struct Model: Identifiable {
    let id = UUID()
    let text = generateRandomText(range: 1...5)
    
    // MARK: - Utils
    
    private static func generateRandomText(range: ClosedRange<Int>) -> String {
        var result = ""
        for _ in 0..<Int.random(in: range) {
            if let sentence = sentences.randomElement() {
                result += sentence
            }
        }
        return result.trimmingCharacters(in: .whitespaces)
    }
    
    private static let sentences = [
        "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
        "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.",
        "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.",
        "Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
    ]
}

and use it like this:

ForEach(models) { model in
    Text(model.text)
         .padding()
         .multilineTextAlignment(.leading)
         .background(Color(from: model.id))
         .id(model.id.uuidString)
}

Then on iOS 26, opening the keyboard makes the scroll position jump unpredictably.

It is more visible if you play with the app, but I could not upload a video here.

Environment

Xcode 26.0.1 - Simulators and devices on iOS 26.0 - 18.0

Questions

  • Is there any known change in ScrollView / scrollPosition(id:) behavior on iOS 26 related to dynamic height content?
  • Am I missing something in the layout setup that makes this layout unstable with variable-height cells?
  • Is there a workaround or recommended approach for keeping scroll position stable when keyboard appears?

Thanks for the post and thanks for the code.

I consistently ask for comprehensive and focused sample projects, as I occasionally encounter missing classes when retrieving the code. In this instance, it appears that ,in the working version, the SquareView object is not defined? So I can't check the not working version. Will be great to have a focused sample with both versions to compare them.

Could you kindly provide the focused and simplified project so that individuals can download and verify the issue?

If so, please share a link to your test project. That'll help us better understand what's going on. If you're not familiar with preparing a test project, take a look at Creating a test project.

Thanks,

Albert Pascual
  Worldwide Developer Relations.

Hi Albert, Thanks for your response and for taking the time to look into this. You’re right — in my original post the SquareView struct was omitted. I’ve now prepared a complete and minimal Xcode project including both versions (fixed-size and dynamic text). You can find it here: https://github.com/Sawyer-815/infinite-scroll The project builds without warnings using Xcode 26.0.1 and reproduces the issue consistently on iOS 26 simulators (iPhone 17 Pro). You can toggle between Fixed size and Dynamic size using the segmented control to compare behaviors. Let me know if you need any additional details or if there’s a better way to structure the test. Thanks again for your time, Sawyer

Thanks for that!! Very useful, I tried Xcode 26.1 beta 3 available at the developer downloads page with a simulator using the keyboard as well and the dynamic latin paragraphs are working well so far for me.

Have you tried that version of Xcode yet to build the app?

Albert Pascual
  Worldwide Developer Relations.

ScrollView &#43; LazyVStack &#43; dynamic height views cause scroll glitches on iOS 26
 
 
Q