I want to create a custom scroll that snaps views to middle But I can't figure out how to set the offset correctly.
This is the sample of the scroll:
struct contentView: View {
@State var startOffset: CGFloat = 0
@State var translationWidth: CGFloat = 0
@State var offset: CGFloat = 0
@State var index: Int = 0
var body: some View {
GeometryReader { geo in
HStack {
ForEach(0..<5, id: \.self) { num in
Button(action: {
// some action
}) {
Circle()
}
.frame(width: geo.size.width)
.offset(x: -self.offset*CGFloat(self.index) + self.translationWidth)
}
}.frame(width: geo.size.width, height: 200)
.offset(x: self.startOffset )
.highPriorityGesture (
DragGesture(minimumDistance: 0.1, coordinateSpace: .global)
.onChanged({ (value) in
self.translationWidth = value.translation.width
})
.onEnded({ (value) in
if self.translationWidth > 40 {
self.index -= 1
} else if self.translationWidth < -40 {
self.index += 1
}
self.translationWidth = 0
})
)
.onAppear {
withAnimation(.none) {
let itemsWidth: CGFloat = CGFloat(geo.size.width*5) - (geo.size.width)
self.offset = geo.size.width
self.startOffset = itemsWidth/2
}
}
}
}
}
It works but it is slightly off the middle, it doesn't scroll exactly in the middle and I don't understand why.
The problem occurs with the onAppear closure:
.onAppear {
withAnimation(.none) {
let itemsWidth: CGFloat = CGFloat(geo.size.width*5) - (geo.size.width)
self.offset = geo.size.width
self.startOffset = itemsWidth/2
}
}
I might be missing some small pixels in my calculations.
UPDATE
So Apparently the HStack has a default value for spacing between views. so to fix it you should remove it like so:
HStack(spacing: 0) {
....
}
TabView's PageTabViewStyle() is another option for scrolling views in SwiftUI that snap to the middle:
struct ContentView: View {
var body: some View {
TabView {
ForEach(0..<5) { _ in
Button(action: {
// some action
}) {
Circle().frame(width: 200, height: 200)
}
}
}
.tabViewStyle(PageTabViewStyle())
}
}
The result looks like this:

So after a bit of experimenting, I realized the HStack is using a default spacing which is not equal to zero. It means that in my calculations I didn't include the spacing between items
so to fix this all you need to add is:
HStack(spacing: 0) {
...
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With