使用UIScrollview可以实现UIAnimator的UISnapBehavior吗?

时间:2016-07-19 18:01:58

标签: ios swift uiscrollview uisnapbehavior

我试图找出是否可以在UIScrollview中使用来自UIAnimator的UISnapBehavior来使滚动视图的内容捕捉到一个点。到目前为止,我的发现导致这是不可能的。

我想要实现的目标

UIScrollView在用户拖动滚动视图时“捕捉”到某个点。但是,滚动必须从快照位置恢复,而用户不必抬起触摸。

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

enter image description here

我尝试过什么

我尝试通过将UIPanGestureRecognizer附加到scrollview并使用它的速度来获得此行为。如果用户正在向捕捉点拖动,滚动视图将禁用滚动,动画到捕捉点,完成动画后它将重新启用滚动。

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

2 个答案:

答案 0 :(得分:4)

我试图模仿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 minDistanceToSnapscrollview velocity with minVelocityToSnap。更改为didSnap州。
  2. didSnap:手动setContentOffset到提供的contextOffset(snapPoint)。在dragDistance上计算scrollView。如果用户拖动超过特定距离(minDragDistanceToReleaseSnap),请更改为willRelease州。
  3. willRelease:如果willSnap超过distance scroll from snapPoint,则再次更改为minDistanceToSnap州。

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

    辅助功能

    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制作的项目。您可能需要更改一些内容才能编译。

答案 1 :(得分:1)

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

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