SwiftUI ScrollView Centered Items with ScrollTargetBehavior
Let's make a ScrollView show a bunch of views horizontally, and center each one as the user scrolls, while also showing edges of adjacent ones. In a hurry to get the entire code? It's at the bottom of the article.
Any time a ScrollView scrolls, it calculates where it's going to finish. You can "sneakily" steal those calculations, modify them, replace the originals, and see scrolling finish where you just told it to.
This gets done by supplying the ScrollView with a ScrollTargetBehavior.
That's going to be a struct adhering to the ScrollTargetBehavior protocol, implementing only one method:
func updateTarget(_ target: inout ScrollTarget, context: TargetContext)
In that method, ScrollTarget is a struct representing where the scrolling will finish. TargetContext will give you various info on the ScrollView — where it was when the gesture began, which way it's scrolling, with what velocity, what the total scrolling content size is, and what the container size (visible view) is.
Because ScrollTarget is an inout parameter, we can modify it and make the ScrollView land somewhere else than originally intended.
Here's what I did to make it center on each view (follow code in gist below, lines 49–53):
- Took the top left coordinate where the scrolling was supposed to land and figured out which view that would be
- Calculated the top left coordinate of that view
- Figured out how much remaining space there is when the view's width is deducted from ScrollView's width on screen
- Made the ScrollView land not on the top left coordinate of the view, but "half the remaining space" before it
Here's the code:
← All development articles