UIScrollViewでスクロールの方向を見つけますか?


183

UIScrollView横スクロールのみが許可されているを持っています。ユーザーがスクロールする方向(左、右)を知りたいのですが。私がやったことは、UIScrollViewtouchesMovedメソッドをオーバーライド:

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    [super touchesMoved:touches withEvent:event];

    UITouch *touch = [touches anyObject];
    float now = [touch locationInView:self].x;
    float before = [touch previousLocationInView:self].x;
    NSLog(@"%f %f", before, now);
    if (now > before){
        right = NO;
        NSLog(@"LEFT");
    }
    else{
        right = YES;
        NSLog(@"RIGHT");

    }

}

しかし、移動したときにこのメソッドがまったく呼び出されない場合があります。どう思いますか?


以下の私の応答を参照してください-これを行うには、スクロールビューデリゲートを使用する必要があります。
10

ここでは最良の答え:stackoverflow.com/questions/11262583/...
iksnae

回答:


392

方向の決定はかなり簡単ですが、方向はジェスチャーの過程で何度も変わる可能性があることに注意してください。たとえば、ページングがオンになっているスクロールビューがあり、ユーザーがスワイプして次のページに移動する場合、最初の方向は右方向になりますが、バウンスがオンになっていると、少しの間、まったく方向を向かず、その後、簡単に左に行きます。

方向を決定するには、UIScrollView scrollViewDidScrollデリゲートを使用する必要があります。このサンプルではlastContentOffset、現在のコンテンツオフセットを以前のコンテンツオフセットと比較するために使用するという名前の変数を作成しました。大きい場合、scrollViewは右にスクロールしています。小さい場合、scrollViewは左にスクロールしています。

// somewhere in the private class extension
@property (nonatomic, assign) CGFloat lastContentOffset;

// somewhere in the class implementation
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {

    ScrollDirection scrollDirection;

    if (self.lastContentOffset > scrollView.contentOffset.x) {
        scrollDirection = ScrollDirectionRight;
    } else if (self.lastContentOffset < scrollView.contentOffset.x) {
        scrollDirection = ScrollDirectionLeft;
    }

    self.lastContentOffset = scrollView.contentOffset.x;

    // do whatever you need to with scrollDirection here.    
}

次の列挙型を使用して方向を定義しています。最初の値をScrollDirectionNoneに設定すると、変数を初期化するときにその方向をデフォルトにするという追加の利点があります。

typedef NS_ENUM(NSInteger, ScrollDirection) {
    ScrollDirectionNone,
    ScrollDirectionRight,
    ScrollDirectionLeft,
    ScrollDirectionUp,
    ScrollDirectionDown,
    ScrollDirectionCrazy,
};

1
lastContentOffsetを取得するにはどうすればよいですか
Dev

@JasonZhao Heh-最初にコードをテストしているときに、scrollviewバウンスがスクロール方向を..er..bounceにすることを考慮していなかったため、奇妙な結果が出ていました。それで、予期しない結果が見つかったときに、それを列挙型に追加しました。
memmons

1
@Devコードに含まれていますlastContentOffset = scrollView.contentOffset.x;
memmons

@ akivag29タイプの良いキャッチlastContentOffset。プロパティ変数と静的変数について:どちらのソリューションも適切な選択です。私は本当の好みはありません。それは良い点です。
memmons 2014

2
@JosueEspinosa正しいスクロール方向を取得するには、それをscrollViewWillBeginDragging:メソッドに入れて、lastContentOffsetセッター呼び出しを行います。
イチゴ

73

...ユーザーがスクロールする方向(左、右)を知りたい。

その場合、iOS 5以降では、UIScrollViewDelegateを使用してユーザーのパンジェスチャーの方向を決定します。

- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{ 
    if ([scrollView.panGestureRecognizer translationInView:scrollView.superview].x > 0) {
        // handle dragging to the right
    } else {
        // handle dragging to the left
    }
}

1
最善の解決策かもしれないが、本当に遅いスタートで、DXは0に等しくなります
trickster77777

もちろんです。これは、主にプロパティに保存された値を使用する必要があるため、より「ネイティブ」または適切な方法であるため、より多くの方法を推奨する必要があります。
Michi

これはstackoverflow.com/a/26192103/62と同じ問題ではありませんか?ユーザーがパンを停止した後、モメンタムスクロールが開始されると正しく機能しなくなりますか?
Liron Yahdav、2016年

59

使用する scrollViewDidScroll:と、現在の方向を見つけることができます。

ユーザーがスクロールを終了したの方向を知りたい場合は、以下を使用します。

@property (nonatomic) CGFloat lastContentOffset;

- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {

    self.lastContentOffset = scrollView.contentOffset.x;
}

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {

    if (self.lastContentOffset < scrollView.contentOffset.x) {
        // moved right
    } else if (self.lastContentOffset > scrollView.contentOffset.x) {
        // moved left
    } else {
        // didn't move
    }
}

3
これはJustinのすばらしい追加ですが、編集を1つ追加したいと思います。スクロールビューでページングが有効になっていて、最終的にその初期位置に戻るドラッグを実行すると、現在の「else」条件では、「変更なし」であっても、「左に移動した」と見なされます。
スコットリーバーマン2013

1
@ScottLiebermanあなたの権利、私はそれに応じてコードを更新しました。
Justin Tanner

50

これを追跡するために追加の変数を追加する必要はありません。このようなUIScrollViewpanGestureRecognizerプロパティを使用するだけです。残念ながら、これは速度が0でない場合にのみ機能します。

CGFloat yVelocity = [scrollView.panGestureRecognizer velocityInView:scrollView].y;
if (yVelocity < 0) {
    NSLog(@"Up");
} else if (yVelocity > 0) {
    NSLog(@"Down");
} else {
    NSLog(@"Can't determine direction as velocity is 0");
}

xおよびyコンポーネントの組み合わせを使用して、上下左右を検出できます。


9
残念ながら、これはドラッグ中に機能します。スクロール中であっても、タッチが解除されると速度は0になります。
heiko

いいね+1。それ以外の場合は速度0の部分です。ユーザーがもうドラッグしていないため、ジェスチャー速度は0です
Pavan

方向を変数に保存できます。速度!= 0の場合にのみ変更します。私にとっては機能します
Leszek Zarna

scrollViewWillBeginDraggingで動作します。素晴らしい
demensdeum

40

ソリューション

func scrollViewDidScroll(scrollView: UIScrollView) {
     if(scrollView.panGestureRecognizer.translationInView(scrollView.superview).y > 0)
     {
         print("up")
     }
    else
    {
         print("down")
    } 
}

1
ではvelocityInViewなくで試してみてくださいtranslationInView。これは、同じ動きで方向を変えるときに発生する問題を解決します。
2016年

2
このソリューションは完璧に機能しています。次の画面に移動して戻ってスクロールアップおよびダウン操作を実行すると、最も受け入れられている回答が機能しないことがあります。
Anjaneyulu Battula

最高、thnx!
ブーハリン

18

スウィフト4:

水平スクロールの場合は、次のようにするだけです。

if scrollView.panGestureRecognizer.translation(in: scrollView.superview).x > 0 {
   print("left")
} else {
   print("right")
}

垂直スクロールの変更のため.x.y


14

iOS8 Swiftでは、この方法を使用しました:

override func scrollViewDidScroll(scrollView: UIScrollView){

    var frame: CGRect = self.photoButton.frame
    var currentLocation = scrollView.contentOffset.y

    if frame.origin.y > currentLocation{
        println("Going up!")
    }else if frame.origin.y < currentLocation{
        println("Going down!")
    }

    frame.origin.y = scrollView.contentOffset.y + scrollHeight
    photoButton.frame = frame
    view.bringSubviewToFront(photoButton)

}

ユーザーがスクロールすると位置が変わる動的ビューがあるので、ビューは画面上の同じ場所にとどまっているように見えます。また、ユーザーが上昇または下降するタイミングも追跡しています。

これも別の方法です:

func scrollViewWillEndDragging(scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
    if targetContentOffset.memory.y < scrollView.contentOffset.y {
        println("Going up!")
    } else {
        println("Going down!")
    }
}

1
yの代わりにxを使用しますか?
Esqarrouth 2017年

実際、UiCollectionViewで検出したいと思います。横スクロールしています。だから私はcrollViewDidEndDecelerating正しく検出しますか?
Umair Afzal 2017

8

これは私にとって(Objective-Cで)うまくいったものです:

    - (void)scrollViewDidScroll:(UIScrollView *)scrollView{

        NSString *direction = ([scrollView.panGestureRecognizer translationInView:scrollView.superview].y >0)?@"up":@"down";
        NSLog(@"%@",direction);
    }

7
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {

    CGPoint targetPoint = *targetContentOffset;
    CGPoint currentPoint = scrollView.contentOffset;

    if (targetPoint.y > currentPoint.y) {
        NSLog(@"up");
    }
    else {
        NSLog(@"down");
    }
}

2
スクロールビューのpagingEnabledプロパティの値がYESの場合、このメソッドは呼び出されません。
赤穂

5

または、キーパス「contentOffset」を監視することもできます。これは、スクロールビューのデリゲートを設定または変更できない場合に役立ちます。

[yourScrollView addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];

オブザーバーを追加した後、次のことができます。

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
    CGFloat newOffset = [[change objectForKey:@"new"] CGPointValue].y;
    CGFloat oldOffset = [[change objectForKey:@"old"] CGPointValue].y;
    CGFloat diff = newOffset - oldOffset;
    if (diff < 0 ) { //scrolling down
        // do something
    }
}

必要に応じてオブザーバーを忘れずに削除してください。例えば、あなたは観察者を追加することができviewWillAppearし、それを削除しviewWillDisappear


5

迅速に:

    func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
    if scrollView.panGestureRecognizer.translation(in: scrollView).y < 0 {
        print("down")
    } else {
        print("up")
    }
}

scrollViewDidScrollでも実行できます。


4

@followbenの答えのような動作の解決策は次のとおりですが、スロースタート(dyが0の場合)で損失なし

@property (assign, nonatomic) BOOL isFinding;
@property (assign, nonatomic) CGFloat previousOffset;

- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
    self.isFinding = YES;
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    if (self.isFinding) {
        if (self.previousOffset == 0) {
            self.previousOffset = self.tableView.contentOffset.y;

        } else {
            CGFloat diff = self.tableView.contentOffset.y - self.previousOffset;
            if (diff != 0) {
                self.previousOffset = 0;
                self.isFinding = NO;

                if (diff > 0) {
                    // moved up
                } else {
                    // moved down
                }
            }
        }
    }
}

1

私はいくつかの回答をチェックし、UIScrollViewカテゴリのドロップにすべてをラップすることでAnswerBotの回答について詳しく説明しました。「lastContentOffset」は代わりにuiscrollview内に保存され、それを呼び出すだけです。

- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
  [scrollView setLastContentOffset:scrollView.contentOffset];
}

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
  if (scrollView.scrollDirectionX == ScrollDirectionRight) {
    //Do something with your views etc
  }
  if (scrollView.scrollDirectionY == ScrollDirectionUp) {
    //Do something with your views etc
  }
}

https://github.com/tehjord/UIScrollViewScrollingDirectionのソースコード


1

@memmonsの回答に基づいて、フィルタリングを行うことを好みます

Objective-Cの場合:

// in the private class extension
@property (nonatomic, assign) CGFloat lastContentOffset;

// in the class implementation
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {

    if (fabs(self.lastContentOffset - scrollView.contentOffset.x) > 20 ) {
        self.lastContentOffset = scrollView.contentOffset.x;
    }

    if (self.lastContentOffset > scrollView.contentOffset.x) {
        //  Scroll Direction Left
        //  do what you need to with scrollDirection here.
    } else {
        //  omitted 
        //  if (self.lastContentOffset < scrollView.contentOffset.x)

        //  do what you need to with scrollDirection here.
        //  Scroll Direction Right
    } 
}

でテストした場合- (void)scrollViewDidScroll:(UIScrollView *)scrollView

NSLog(@"lastContentOffset: --- %f,   scrollView.contentOffset.x : --- %f", self.lastContentOffset, scrollView.contentOffset.x);

img

self.lastContentOffset 非常に速く変化し、値のギャップはほぼ0.5fです。

それは必要ない。

そして、時々、正確な状態で取り扱われると、あなたの向きが失われる可能性があります。(実装ステートメントがスキップされる場合があります)

といった :

- (void)scrollViewDidScroll:(UIScrollView *)scrollView{

    CGFloat viewWidth = scrollView.frame.size.width;

    self.lastContentOffset = scrollView.contentOffset.x;
    // Bad example , needs value filtering

    NSInteger page = scrollView.contentOffset.x / viewWidth;

    if (page == self.images.count + 1 && self.lastContentOffset < scrollView.contentOffset.x ){
          //  Scroll Direction Right
          //  do what you need to with scrollDirection here.
    }
   ....

Swift 4の場合:

var lastContentOffset: CGFloat = 0

func scrollViewDidScroll(_ scrollView: UIScrollView) {

     if (abs(lastContentOffset - scrollView.contentOffset.x) > 20 ) {
         lastContentOffset = scrollView.contentOffset.x;
     }

     if (lastContentOffset > scrollView.contentOffset.x) {
          //  Scroll Direction Left
          //  do what you need to with scrollDirection here.
     } else {
         //  omitted
         //  if (self.lastContentOffset < scrollView.contentOffset.x)

         //  do what you need to with scrollDirection here.
         //  Scroll Direction Right
    }
}

0

ページングがオンになっている場合、これらのコードを使用できます。

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
    self.lastPage = self.currentPage;
    CGFloat pageWidth = _mainScrollView.frame.size.width;
    self.currentPage = floor((_mainScrollView.contentOffset.x - pageWidth / 2) / pageWidth) + 1;
    if (self.lastPage < self.currentPage) {
        //go right
        NSLog(@"right");
    }else if(self.lastPage > self.currentPage){
        //go left
        NSLog(@"left");
    }else if (self.lastPage == self.currentPage){
        //same page
        NSLog(@"same page");
    }
}

0

コードは私が推測するそれ自体を説明します。同じクラスのプライベートインターフェイスで宣言されたCGFloat差分1と差分2。contentSizeが変わらない場合に適しています。

- (void)scrollViewDidScroll:(UIScrollView *)scrollView
        {

        CGFloat contentOffSet = scrollView.contentOffset.y;
        CGFloat contentHeight = scrollView.contentSize.height;

        difference1 = contentHeight - contentOffSet;

        if (difference1 > difference2) {
            NSLog(@"Up");
        }else{
            NSLog(@"Down");
        }

        difference2 = contentHeight - contentOffSet;

       }

0

わかりましたので、私にとってこの実装は本当にうまくいきます:

@property (nonatomic, assign) CGPoint lastContentOffset;


- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
    _lastContentOffset.x = scrollView.contentOffset.x;
    _lastContentOffset.y = scrollView.contentOffset.y;

}


- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {

    if (_lastContentOffset.x < (int)scrollView.contentOffset.x) {
        // moved right
        NSLog(@"right");
    }
    else if (_lastContentOffset.x > (int)scrollView.contentOffset.x) {
        // moved left
        NSLog(@"left");

    }else if (_lastContentOffset.y<(int)scrollView.contentOffset.y){
        NSLog(@"up");

    }else if (_lastContentOffset.y>(int)scrollView.contentOffset.y){
        NSLog(@"down");
        [self.txtText resignFirstResponder];

    }
}

ドラッグが終了すると、textViewが呼び出されて終了します


0
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {
NSLog(@"px %f py %f",velocity.x,velocity.y);}

scrollviewのこのデリゲートメソッドを使用します。

速度のy座標が+ veの場合、スクロールビューは下にスクロールし、-veの場合、スクロールビューは上にスクロールします。同様に、左右のスクロールはx座標を使用して検出できます。


0

Short&Easyは、速度の値を確認します。速度がゼロより大きい場合は、左にスクロールし、それ以外の場合は右にスクロールします。

func scrollViewWillEndDragging(scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {

    var targetOffset = Float(targetContentOffset.memory.x)
    println("TargetOffset: \(targetOffset)")
    println(velocity)

    if velocity.x < 0 {
        scrollDirection = -1 //scrolling left
    } else {
        scrollDirection = 1 //scrolling right
    }
}

0

UIScrollViewとUIPageControlを使用する場合、このメソッドはPageControlのページビューも変更します。

  func scrollViewWillEndDragging(scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {

    let targetOffset = targetContentOffset.memory.x
    let widthPerPage = scrollView.contentSize.width / CGFloat(pageControl.numberOfPages)

    let currentPage = targetOffset / widthPerPage
    pageControl.currentPage = Int(currentPage)
}

@EsqのSwiftコードに感謝します。


0

Swift 2.2 単一のソリューションと複数の方向 を失うことなく追跡するシンプルなソリューション

  // Keep last location with parameter
  var lastLocation:CGPoint = CGPointZero

  // We are using only this function so, we can
  // track each scroll without lose anyone
  override func scrollViewWillBeginDragging(scrollView: UIScrollView) {
    let currentLocation = scrollView.contentOffset

    // Add each direction string
    var directionList:[String] = []

    if lastLocation.x < currentLocation.x {
      //print("right")
      directionList.append("Right")
    } else if lastLocation.x > currentLocation.x {
      //print("left")
      directionList.append("Left")
    }

    // there is no "else if" to track both vertical
    // and horizontal direction
    if lastLocation.y < currentLocation.y {
      //print("up")
      directionList.append("Up")
    } else if lastLocation.y > currentLocation.y {
      //print("down")
      directionList.append("Down")
    }

    // scrolled to single direction
    if directionList.count == 1 {
      print("scrolled to \(directionList[0]) direction.")
    } else if directionList.count > 0  { // scrolled to multiple direction
      print("scrolled to \(directionList[0])-\(directionList[1]) direction.")
    }

    // Update last location after check current otherwise,
    // values will be same
    lastLocation = scrollView.contentOffset
  }

0

すべての上の答えで、問題を解決する2つの主要な方法は、panGestureRecognizerまたはを使用することですcontentOffset。どちらの方法にも短所と長所があります。

方法1:panGestureRecognizer

panGestureRecognizer@followbenの提案のように使用する場合、プログラムでスクロールビューをスクロールしたくない場合は、適切に動作します。

- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{ 
    if ([scrollView.panGestureRecognizer translationInView:scrollView.superview].x > 0) {
        // handle dragging to the right
    } else {
        // handle dragging to the left
    }
}

短所

しかし、次のコードでスクロールビューを移動すると、上のコードはそれを認識できません。

setContentOffset(CGPoint(x: 100, y: 0), animation: false)

方法2:contentOffset

var lastContentOffset: CGPoint = CGPoint.zero

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    if (self.lastContentOffset.x > scrollView.contentOffset.x) {
        // scroll to right
    } else if self.lastContentOffset.x < scrollView.contentOffset.x {
        // scroll to left
    }
    self.lastContentOffset = self.scrollView.contentOffset
}

短所

スクロール中に(無限スクロールを作成する場合など)プログラムでcontentOffsetを変更する場合、contentOffsetコンテンツビューの場所の変更中に変更する可能性があるため、このメソッドは問題を引き起こします。左。


0
//Vertical detection
    var lastVelocityYSign = 0
            func scrollViewDidScroll(_ scrollView: UIScrollView) {
                let currentVelocityY =  scrollView.panGestureRecognizer.velocity(in: scrollView.superview).y
                let currentVelocityYSign = Int(currentVelocityY).signum()
                if currentVelocityYSign != lastVelocityYSign &&
                    currentVelocityYSign != 0 {
                    lastVelocityYSign = currentVelocityYSign
                }
                if lastVelocityYSign < 0 {
                    print("SCROLLING DOWN")
                } else if lastVelocityYSign > 0 {
                    print("SCOLLING UP")
                }
            }

Mos6y https://medium.com/@Mos6yCanSwift/swift-ios-determine-scroll-direction-d48a2327a004 からの回答

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.