Skip to content

Commit b4e8030

Browse files
authored
Update README.md
1 parent 2faa057 commit b4e8030

File tree

1 file changed

+77
-0
lines changed

1 file changed

+77
-0
lines changed

README.md

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,79 @@
11
# SnapDrag
22
Snap behavior in UIScrollView in Swift
3+
4+
Needed this to answer a StackOverflow question:<br>
5+
[UIAnimator's UISnapBehavior possible with UIScrollview?](http://stackoverflow.com/q/38465498/1378447)
6+
7+
###Question
8+
What I am Trying to Achieve
9+
10+
UIScrollView to 'snap' to certain point while the user is dragging the scroll view. However, the scrolling has to resume from snapped position without the user having to lift the touch.
11+
12+
Apple seems to achieve this in Photo Editing in their iOS Photos App. (See screenshot below)
13+
14+
<p align="center"><img src="http://i.stack.imgur.com/1k57B.png"/></p>
15+
16+
###Answer
17+
I've tried to mimic iOS Photos app. Here is my logic:
18+
19+
// CALCULATE A CONTENT OFFSET FOR SNAPPING POINT
20+
let snapPoint = CGPoint(x: 367, y: 0)
21+
22+
// CHANGE THESE VALUES TO TEST
23+
let minDistanceToSnap = 7.0
24+
let minVelocityToSnap = 25.0
25+
let minDragDistanceToReleaseSnap = 7.0
26+
let snapDuringDecelerating = false
27+
28+
29+
This kind of scrolling needs 3 stages
30+
31+
enum SnapState {
32+
case willSnap
33+
case didSnap
34+
case willRelease
35+
}
36+
37+
1. `willSnap:` Default state. Decide when to snap. Compare `contentOffset distance from SnapPoint with minDistanceToSnap` and `scrollview velocity with minVelocityToSnap`. Change to `didSnap` state.
38+
2. `didSnap:` Manually `setContentOffset` to a provided `contextOffset(snapPoint)`. Calculate `dragDistance` on `scrollView`. If user drag more than a certain distance (`minDragDistanceToReleaseSnap`) change to `willRelease` state.
39+
3. `willRelease:` Change to `willSnap` state again if `distance scroll from snapPoint` is more than `minDistanceToSnap`.
40+
41+
<br>
42+
43+
extension ViewController: UIScrollViewDelegate {
44+
func scrollViewDidScroll(scrollView: UIScrollView) {
45+
switch(snapState) {
46+
case .willSnap:
47+
let distanceFromSnapPoint = distance(between: scrollView.contentOffset, and: snapPoint)
48+
let velocity = scrollView.panGestureRecognizer.velocityInView(view)
49+
let velocityDistance = distance(between: velocity, and: CGPointZero)
50+
if distanceFromSnapPoint <= minDistanceToSnap && velocityDistance <= minVelocityToSnap && (snapDuringDecelerating || velocityDistance > 0.0) {
51+
startSnapLocaion = scrollView.panGestureRecognizer.locationInView(scrollView)
52+
snapState = .didSnap
53+
}
54+
case .didSnap:
55+
scrollView.setContentOffset(snapPoint, animated: false)
56+
var dragDistance = 0.0
57+
let location = scrollView.panGestureRecognizer.locationInView(scrollView)
58+
dragDistance = distance(between: location, and: startSnapLocaion)
59+
if dragDistance > minDragDistanceToReleaseSnap {
60+
startSnapLocaion = CGPointZero
61+
snapState = .willRelease
62+
}
63+
case .willRelease:
64+
let distanceFromSnapPoint = distance(between: scrollView.contentOffset, and: snapPoint)
65+
if distanceFromSnapPoint > minDistanceToSnap {
66+
snapState = .willSnap
67+
}
68+
}
69+
}
70+
}
71+
72+
73+
Helper function
74+
75+
func distance(between point1: CGPoint, and point2: CGPoint) -> Double {
76+
return Double(hypotf(Float(point1.x - point2.x), Float(point1.y - point2.y)))
77+
}
78+
79+

0 commit comments

Comments
 (0)