I tried to simulate the iOS Photos app. Here is my logic:
// CALCULATE A CONTENT OFFSET FOR SNAPPING POINT let snapPoint = CGPoint(x: 367, y: 0) // CHANGE THESE VALUES TO TEST let minDistanceToSnap = 7.0 let minVelocityToSnap = 25.0 let minDragDistanceToReleaseSnap = 7.0 let snapDuringDecelerating = false
This type of scroll requires 3 steps.
enum SnapState { case willSnap case didSnap case willRelease }
willSnap: The default state. Decide when to click. Compare contentOffset distance from SnapPoint with minDistanceToSnap and scrollview velocity with minVelocityToSnap . Switch to didSnap state.didSnap: Manually setContentOffset to the provided contextOffset(snapPoint) . Compute dragDistance on scrollView . If the user has dragged more than a certain distance ( minDragDistanceToReleaseSnap ) to willRelease state.willRelease: Replace the willSnap value if the distance scroll from snapPoint greater than minDistanceToSnap .
extension ViewController: UIScrollViewDelegate { func scrollViewDidScroll(scrollView: UIScrollView) { switch(snapState) { case .willSnap: let distanceFromSnapPoint = distance(between: scrollView.contentOffset, and: snapPoint) let velocity = scrollView.panGestureRecognizer.velocityInView(view) let velocityDistance = distance(between: velocity, and: CGPointZero) if distanceFromSnapPoint <= minDistanceToSnap && velocityDistance <= minVelocityToSnap && (snapDuringDecelerating || velocityDistance > 0.0) { startSnapLocaion = scrollView.panGestureRecognizer.locationInView(scrollView) snapState = .didSnap } case .didSnap: scrollView.setContentOffset(snapPoint, animated: false) var dragDistance = 0.0 let location = scrollView.panGestureRecognizer.locationInView(scrollView) dragDistance = distance(between: location, and: startSnapLocaion) if dragDistance > minDragDistanceToReleaseSnap { startSnapLocaion = CGPointZero snapState = .willRelease } case .willRelease: let distanceFromSnapPoint = distance(between: scrollView.contentOffset, and: snapPoint) if distanceFromSnapPoint > minDistanceToSnap { snapState = .willSnap } } } }
Helper function
func distance(between point1: CGPoint, and point2: CGPoint) -> Double { return Double(hypotf(Float(point1.x - point2.x), Float(point1.y - point2.y))) }
Made a demo project on Github: https://github.com/rishi420/SnapDrag
Note. The project was completed using Xcode 7.2. You may need to change the bit to compile.
source share