使用UIScrollview可以使用UIAnimator的UISnapBehavior?

我想弄清楚,如果我可以在UIScrollview中使用来自UIAnimator的UISnapBehavior使滚动视图的内容捕捉到一个点。 到目前为止,我的发现导致了这是不可能的。

我正在努力实现的

当用户拖动滚动视图时,UIScrollView将“捕捉”到某一点。 但是,滚动必须从卡扣位置恢复,而用户不必提起触摸。

苹果似乎在iOS照片应用程序的照片编辑中实现了这一点。 (见下面的截图)

在这里输入图像说明

我所试过的

我尝试通过将UIPanGestureRecognizer附加到滚动视图并使用它的速度来获得此行为。 如果用户正在向捕捉点拖动,那么滚动视图将禁用滚动,在捕捉animation的时候animation,这将重新启用滚动。

但是,这会导致用户在拖动后抬起触摸并重新拖动滚动视图的问题。 然而,苹果似乎已经做到了,而不必解除阻力。

我试图模仿iOS的照片应用程序。 这是我的逻辑:

// 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 

这种滚动需要3个阶段

 enum SnapState { case willSnap case didSnap case willRelease } 
  1. willSnap:默认状态。 决定何时拍摄。 contentOffset distance from SnapPoint with minDistanceToSnap比较contentOffset distance from SnapPoint with minDistanceToSnapscrollview velocity with minVelocityToSnap 。 更改为didSnap状态。
  2. didSnap:手动将setContentOffset设置为提供的contextOffset(snapPoint) 。 在dragDistance上计算scrollView 。 如果用户拖动超过一定的距离( minDragDistanceToReleaseSnap )更改为willRelease状态。
  3. willRelease:如果willSnap distance scroll from snapPoint大于minDistanceToSnap则再次更改为willSnap状态。

 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 } } } } 

助手function

 func distance(between point1: CGPoint, and point2: CGPoint) -> Double { return Double(hypotf(Float(point1.x - point2.x), Float(point1.y - point2.y))) } 

在Github上做了一个演示项目: https : //github.com/rishi420/SnapDrag

注意:使用Xcode 7.2制作的项目。 您可能需要更改一下才能编译。

不要将UIPanGestureRecognizer直接添加到UIScrollView。 而是将其添加到容器视图,然后在select器中手动设置UIScrollView contentOffset。

禁用UIScrollView本身的交互,或者使用委托来防止直接与滚动视图交互。