I have a solution that allows for fast scrolling, and snaps when the speed is slow, it looks like this: https://imgur.com/a/r7Etltu
We need 4 variables:
var dragging = false
var lastOffset: CGPoint = .zero
var lastOffsetCapture: TimeInterval = .zero
var isScrollingFast: Bool = false
We need a helper function:
extension UIScrollView {
var currentPage:Int{
return Int((self.contentOffset.x+(0.5*self.frame.size.width))/self.frame.width)
}
}
Then we need delegate methods:
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let currentOffset = scrollView.contentOffset
let currentTime = Date.timeIntervalSinceReferenceDate
let timeDiff = currentTime - lastOffsetCapture
let captureInterval = 0.1
if timeDiff > captureInterval {
let distance = currentOffset.x - lastOffset.x
let scrollSpeedNotAbs = (distance * 10) / 1000
let scrollSpeed = fabsf(Float(scrollSpeedNotAbs))
if scrollSpeed > 0.3 {
isScrollingFast = true
} else {
if isScrollingFast && !dragging && scrollSpeed > 0.1 {
isScrollingFast = false
snapCard()
}
}
lastOffset = currentOffset
lastOffsetCapture = currentTime
}
}
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
dragging = true
}
func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
dragging = false
let currentPage = scrollView.currentPage
let pageSize = YOUR PAGE SIZE
if velocity.x == 0 {
let rightOfCenter = scrollView.contentOffset.x - currentPage*pageSize > 0
snapCard(force: true, moveRight: rightOfCenter)
return
}
if fabsf(Float(velocity.x)) < 0.3 {
snapCard(force: true, moveRight: velocity.x > 0)
}
}
func snapCard(force: Bool = false, moveRight: Bool = true) {
let currentPage = scrollView.currentPage
let pageSize = YOUR PAGE SIZE
if !force {
scrollView.setContentOffset(CGPoint(x: currentPage * pageSize, y: 0), animated: true)
} else {
if currentPage != YOUR NUM PAGES -1 {
scrollView.setContentOffset(CGPoint(x: pageSize *(currentPage + (moveRight ? 1 : -1)), y: 0), animated: true)
}
}
}