The ScrollPosition type (iOS 18) drives a ScrollView programmatically by edge, view id, or
exact point, through one binding. It supersedes id-only tracking with
scrollPosition(id:) when you need more than "which view is visible".
Binding the Position
Bind the state with scrollPosition and call scrollTo on it. Attaching animation with the
position as its value animates every programmatic scroll.
@State private var position = ScrollPosition(edge: .top)
ScrollView {
Button("Scroll to bottom") {
position.scrollTo(edge: .bottom)
}
ForEach(1..<100) { index in
Text(verbatim: index.formatted())
.id(index)
}
}
.scrollPosition($position)
.animation(.default, value: position)
The scrollTo Overloads
scrollTo(edge:)jumps to.topor.bottom.scrollTo(id:anchor:)targets a view id;anchorpicks which point of that view lands visible, such as.center.scrollTo(point:)takes aCGPointoffset into the content.scrollTo(x:)andscrollTo(y:)move one axis and leave the other alone.
position.scrollTo(id: 42, anchor: .center)
position.scrollTo(point: CGPoint(x: 0, y: 100))
position.scrollTo(y: 100)
Reading It Back, and the Gotcha
The position exposes optional edge, point, and viewID values, but only for programmatic
scrolls. As soon as the user drags, those properties become nil;
isPositionedByUser tells you that happened. To observe the live offset during a user gesture,
use onScrollGeometryChange instead; ScrollPosition cannot do it.