How it works
ScrollPhaseChangeContext carries the supplementary information SwiftUI hands you at the instant a scroll view moves between phases — for example from interacting to decelerating, or from animating back to idle. A phase transition alone tells you that the scroll state changed, but not the conditions surrounding it; the context fills that gap by exposing the geometry and momentum at the moment of the change. Reach for it inside a phase-change callback whenever your response needs to depend on more than the phase value itself, such as how fast the content was moving or where it had scrolled to.
Receive the context from onScrollPhaseChange(_:)
The context is delivered as the third parameter of the closure you pass to the scroll view's phase-change modifier. SwiftUI invokes the closure on every transition and supplies the old phase, the new phase, and the surrounding
context. In the example,.onScrollPhaseChange { _, newPhase, context in ... }ignores the previous phase, recordsnewPhase, and keepscontextavailable for the rest of the body.Read momentum with the velocity property
ScrollPhaseChangeContext exposes the scroll view's velocity at the transition, letting you distinguish a gentle settle from a fast flick when deciding how to react. The example touches
context.velocityto show where that signal lives; a real handler might branch on its magnitude to trigger different behavior for slow versus rapid scrolling.Pair the phase with the context to drive state
The value is meant to be consumed alongside the new phase rather than in isolation — the phase says what happened, the context says under what conditions. Here the closure writes
phase = "\(newPhase)"into@Stateso theText("Scroll phase: \(phase)")label updates live, whilecontextstands ready to refine that response when the transition warrants it.Inspect scroll geometry through the context
Beyond velocity, the context bundles the scroll geometry in effect at the change, so handlers that need the current offset or content size can read it without separately tracking position. This makes ScrollPhaseChangeContext the single place to look for the full state of the scroll view at a transition, rather than stitching together several observers.
.onScrollPhaseChange closure, append the momentum to the label with phase = "\(newPhase) v=\(context.velocity)" and flick the list — the velocity reading spikes during the decelerating phase and falls to zero as it returns to idle.Example & preview
Press Run live & edit to compile it in your browser — then edit the Swift on the left and the preview re-renders live.
struct ScrollPhaseChangeContextDemo: View {
@State private var phase = "idle"
var body: some View {
VStack(spacing: 12) {
Text("Scroll phase: \(phase)")
.font(.headline)
ScrollView {
ForEach(0..<20) { i in
Text("Row \(i)")
.frame(maxWidth: .infinity)
.padding(8)
.background(.quaternary)
}
}
.onScrollPhaseChange { _, newPhase, context in
phase = "\(newPhase)"
_ = context.velocity
}
}
.padding()
}
}