Created
August 8, 2025 18:11
-
-
Save bryan1anderson/390ab1fd43d2a028dec22f8fa66c169e to your computer and use it in GitHub Desktop.
SwiftUI ScrollView ScrollPosition Anchor is broken
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import SwiftUI | |
| /** | |
| This view demonstrates that scroll anchors are IGNORED when using `.scrollPosition(...).scrollTo(id:anchor:)`. | |
| - Using `.scrollViewProxy.scrollTo(id:anchor:)` works correctly, anchors are respected. | |
| - Using `.scrollPosition.scrollTo(id:anchor:)` ignores the anchor, always positions the target at the same place. | |
| Use this as a proof-of-bug or platform inconsistency. | |
| */ | |
| struct ContentView: View { | |
| @State private var scrollPosition = ScrollPosition(idType: Int.self) | |
| /// Helper that flips the intended anchor to demonstrate that anchors do not work | |
| var shouldBeTopAnchor: Bool { | |
| guard let id = scrollPosition.viewID as? Int else { return true } | |
| if id <= 50 { | |
| return true | |
| } else { | |
| return false | |
| } | |
| } | |
| var body: some View { | |
| VStack { | |
| Text("PROOF: .scrollPosition.scrollTo ignores anchor, .scrollViewProxy respects anchor.\nTap the button below, and compare the behavior with tap-to-scroll in the list items.") | |
| .foregroundColor(.red) | |
| .multilineTextAlignment(.center) | |
| .padding(.bottom) | |
| Button("[BUG] .scrollPosition.scrollTo(50, anchor: \(shouldBeTopAnchor ? ".top" : ".center")) → Anchor is IGNORED") { | |
| // This should scroll to item 50 with the requested anchor, but SCROLLPOSITION IGNORES THE ANCHOR. | |
| if shouldBeTopAnchor { | |
| // Expected: Scroll item 50 to top, but actually scrolls to bottom | |
| scrollPosition.scrollTo(id: 50, anchor: .top) | |
| } else { | |
| // Expected: Scroll item 50 to center, but actually scrolls to top | |
| scrollPosition.scrollTo(id: 50, anchor: .center) | |
| } | |
| } | |
| // Section: Working Example – ScrollViewProxy respects anchor | |
| ScrollViewReader { proxy in | |
| ScrollView { | |
| LazyVStack(spacing: 12) { | |
| ForEach(0..<100, id: \.self) { index in | |
| Text("Item \(index). Tap me!") | |
| .frame(maxWidth: .infinity, minHeight: 44) | |
| .background(index == 50 ? Color.blue.opacity(0.2) : Color.secondary.opacity(0.1)) | |
| .cornerRadius(8) | |
| .id(index) | |
| .onTapGesture { | |
| // [WORKS] proxy.scrollTo(50, anchor: .center) correctly centers the item. | |
| proxy.scrollTo(50, anchor: .center) | |
| } | |
| } | |
| } | |
| .scrollTargetLayout() | |
| } | |
| } | |
| } | |
| // Section: Broken Example – .scrollPosition ignores anchor | |
| .scrollPosition($scrollPosition) | |
| } | |
| } | |
| #Preview { | |
| ContentView() | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment