Skip to content

ScrollView Scroll Position (SwiftUI)

A SwiftUI guide for coding agents. Also covers scrollposition binding, programmatic scroll ios, initial scroll anchor, scroll to item without scrollviewreader, scrollindicatorsflash, track visible item.

The scrollPosition modifier, new in iOS 17, controls where a ScrollView starts and which item is visible, without ScrollViewReader. A companion, scrollIndicatorsFlash, blinks the indicator on demand.

Initial Anchor

scrollPosition(initialAnchor:) sets where the content sits on first layout. The anchor is a UnitPoint: use a preset such as .center or .bottom, or build one from normalized 0 to 1 coordinates.

ScrollView {
    LazyVStack { /* ... */ }
}
.scrollPosition(initialAnchor: .init(x: 0, y: 0.9))

Reading and Writing the Position

The id: variant binds a hashable value. Scrolling writes the first visible view's identity into the binding; setting the binding scrolls to that view.

struct ContentView: View {
    @State private var position: Int?

    var body: some View {
        ScrollView {
            LazyVStack {
                Button("Jump...") { position = 99 }

                ForEach(0..<100) { index in
                    Rectangle()
                        .fill(Color.green.gradient)
                        .frame(height: 300)
                        .id(index)
                }
            }
            .scrollTargetLayout()
        }
        .scrollPosition(id: $position)
    }
}

Two requirements, both easy to miss:

  • The content needs identities. ForEach over identifiable data provides them; otherwise set .id(_:) explicitly.
  • The container needs scrollTargetLayout (or scrollTarget), or the ScrollView cannot find the identifiers and the binding never updates.

Flashing the Indicator

scrollIndicatorsFlash(trigger:) blinks the scroll indicator whenever the passed equatable value changes, for example after appending content, so the user sees there is more to scroll.

.scrollIndicatorsFlash(trigger: trigger)

Use this guidance in your coding agent

Install the Better Design MCP once. Your agent then loads this page with one call.

get-swiftui-guide({ topic: "swiftui-mastering-scrollview-scroll-position" })
claude mcp add --scope user better-design --transport http https://better-design.com/api/mcp --header "Authorization: Bearer <YOUR_API_KEY>"
Browse related design systems