In iOS17, Swift UI has some new modifiers which make it easier to implement snap behaviour in scrollviews.
ScrollView
Let's make a simple scrollview:
import SwiftUI struct ContentView: View { var body: some View { ScrollView(.horizontal) { LazyHStack { ForEach(0..<20) { i in TileView(title: String(i)) } } .padding() } }} struct TileView: View { let title: String var body: some View { Text(title) .foregroundStyle(.white) .frame(width: 150, height: 100) .background(.purple) .clipShape(.rect(cornerRadius: 20)) } } #Preview { ContentView() }
Reminder: Using LazyHStack
instead of HStack
means the tiles are only loaded when needed and not upfront.
This gives us a simple horizontal scrollview:
The default behaviour is free scrolling, and the view stays wherever it finishes after scrolling:
Paging Behaviour
We can add the modifier .scrollTargetBehavior(.paging)
to our scrollview:
struct ContentView: View { var body: some View { ScrollView(.horizontal) { LazyHStack { ForEach(0..<20) { i in TileView(title: String(i)) } } .padding() } .scrollTargetBehavior(.paging) } }
This means the scrollview will scroll one screen width at a time and snaps nicely to each page:
View Aligned Behaviour
Instead of using paging, we can use the modifier .scrollTargetBehavior(.viewAligned)
along with .scrollTargetLayout()
on the view we want to be used for settling.
struct ContentView: View { var body: some View { ScrollView(.horizontal) { LazyHStack { ForEach(0..<20) { i in TileView(title: String(i)) } } .padding() .scrollTargetLayout() } .scrollTargetBehavior(.viewAligned) } }
This gives us the the following behaviour where the scrollview snaps to the individual views:
Additional Notes
This can all be applied to vertical scrollviews as well. I've just used horizontal ones here as examples.
These modifiers are only available in iOS 17 / Xcode 15
More information and options to experiment with in Apple's docs https://developer.apple.com/documentation/swiftui/scrolltargetbehavior
Top comments (0)