2016-07-19 9 views
2

UIScrollview内のUIAnimatorからUISnapBehaviorを使用して、スクロールビューのコンテンツスナップをポイントにすることができるかどうかを調べようとしています。これまでのところ私の発見はこれが不可能であることにつながっています。私は、ユーザーがスクロールビューをドラッグしている間、特定のポイントに「スナップ」にUIAnimatorのUISnapBehaviorはUIScrollviewで可能ですか?

UIScrollViewのを達成しようとしています何

。しかし、スクロールは、ユーザがタッチを持ち上げる必要なしに、スナップ位置から再開しなければならない。

アップルは、iOSフォトアプリケーションで写真編集でこれを達成しているようです。私はscrollviewにUIPanGestureRecognizerを取り付け、それは速度だ使って、この動作を獲得しようとした

を試してみました何

enter image description here

(下のスクリーンショットを参照してください)。ユーザーがスナップポイントに向かってドラッグしている場合、スクロールビューではスクロールが無効になり、スナップポイントにアニメートされます。アニメーションが完了すると、スクロールが再び有効になります。

しかし、これは、ユーザーがドラッグした後にタッチアップしてスクロールビューを再ドラッグする必要があるという問題を引き起こします。しかし、アップルはドラッグを持ち上げることなくそれをやったようだ。

+0

解決方法を見るhttps://stackoverflow.com/a/48493973/1275014 – thedeveloper3124

答えて

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)scrollViewdragDistanceを計算します。ユーザーが特定の距離(minDragDistanceToReleaseSnap)以上ドラッグした場合、willRelease状態になります。
  3. willRelease:distance scroll from snapPointminDistanceToSnapより大きい場合は、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 
       } 
     } 
    } 
} 

ヘルパー機能のGithub上のデモプロジェクト製

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

https://github.com/rishi420/SnapDrag

注:Xcodeの7.2で作ったプロジェクト。コンパイルするにはビットを変更する必要があるかもしれません。

+1

これは私の既存のコードで動作します。ブラボー! – Gizmodo

1

UIPanGestureRecognizerを直接UIScrollViewに追加しないでください。代わりにコンテナビューに追加し、セレクタでUIScrollView contentOffsetを手動で設定します。

UIScrollView自体のインタラクションを無効にするか、またはデリゲートを使用してスクロールビューとの直接的なやり取りを防止します。

関連する問題