2

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

我正在努力实现的目标

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

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

在此处输入图像描述

我试过的

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

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

4

2 回答 2

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 snapPointminDistanceToSnap


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

于 2016-07-26T13:36:56.843 回答
1

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

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

于 2016-07-23T00:18:21.170 回答