Tracking ScrollView Offsets in SwiftUI: Why It’s a Game Changer
SwiftUI makes UI development feel magical—until you try to build a sticky header, hide a toolbar on scroll, or animate something based on scroll position. 😅
That’s when you realize:
Tracking scroll offset in SwiftUI isn’t built-in and workarounds? Often brittle… or just plain weird.
Thankfully, Anton Gubarenko shared a lightweight solution that gives you full control without hacks—and we’ve been playing with it all week.
Here’s why this trick is a must-have in your SwiftUI toolkit.
Make your app feel smarter with subtle, reactive scroll effects
The Problem
Want to…
Shrink your header as you scroll down?
Animate an element based on scroll progress?
Hide a tab bar after a few swipes?
In UIKit, you’d hook into UIScrollViewDelegate. In SwiftUI? Not so fast.
SwiftUI’s ScrollView doesn’t expose scroll offset out-of-the-box. And while there are GeometryReader hacks or PreferenceKeys floating around, they often feel fragile and hard to reuse.
The Elegant Fix: Offset Observers
Anton’s approach leverages background GeometryReader inside a ScrollView, wrapped in a reusable ScrollViewOffsetObserver.
In short, you measure the position of a hidden proxy view at the top (or bottom) of your scrollable content and bind its offset to a state value.
ScrollView {
GeometryReader { geo in
Color.clear
.preference(key: ScrollOffsetKey.self, value: geo.frame(in: .global).minY)
}
.frame(height: 0)
VStack(spacing: 20) {
// Your content here
}
}
.onPreferenceChange(ScrollOffsetKey.self) { newOffset in
scrollOffset = newOffset
}
Now you’ve got scrollOffset—a live, reactive value you can feed into animations, layout changes, or logic triggers. Clean. Lightweight. Reusable.
Use Cases We Love
Once you unlock scroll offsets, you unlock so much more:
Use Case | Result |
---|---|
Animated hero headers | Shrink image + blur title based on scroll |
Auto-hiding toolbars | Hide/show elements with scroll direction |
Progress indicators | Animate a progress bar across scroll distance |
Sticky navigation | Pin views to top/bottom on trigger thresholds |
Lottie or image-sequence animation | Sync animations with scroll motion |
The best part? No UIKit bridging required.
Pro Tips
Works for both vertical and horizontal ScrollViews
Fully SwiftUI-native—no need for UIViewRepresentable
Avoid nested GeometryReaders where possible—they can mess with layout
For more precision, track multiple anchor points—not just .minY
Want to generalize it? Wrap it in a custom ScrollOffsetReader view modifier or view model logic. Your teammates will thank you.
Real Talk: Why It Matters
Great UX is about feel.
Smooth transitions, smart layout responses, and tiny interactions that respect user flow.
This offset tracking trick gives your app that next level of interactivity—without compromising on SwiftUI purity.
And as Apple continues to evolve SwiftUI APIs (hello, WWDC 2025 👀), solutions like these help bridge the gap in the meantime.
💬 Over to You
Are you using scroll tracking in your app?
Have you found another elegant solution for sticky headers or scroll-based animations?
📣 We want to hear about it—tag us on X with your best scroll tricks.
🔗 Related Reads from the Community
Dynamic Accent Colors in SwiftUI – Theme smarter, not harder
Let vs Var in Swift Structs – Make immutability intentional
IAP vs Web Checkout – RevenueCat’s take on conversion strategy
Swiftly Developed is your companion in mastering Apple development—code-first, community-driven, and refreshingly honest.
📬 Subscribe to our weekly digest and stay ahead of the SwiftUI curve.