Post

Replies

Boosts

Views

Activity

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?
7
0
357
2d