web-dev-qa-db-ja.com

UIScrollView:水平方向のページング、垂直方向のスクロール?

ページングとスクロールがオンになっているUIScrollViewを、特定の瞬間に垂直または水平にのみ移動させるにはどうすればよいですか?

私の理解では、directionalLockEnabledプロパティでこれを実現する必要がありますが、斜めのスワイプにより、モーションが単一の軸に制限されるのではなく、斜めにスクロールします。

編集:より明確にするために、ユーザーが水平にスクロールできるようにしたいと思いますOR同時に、両方ではありません。

83
NickD

あなたは非常に厳しい状況にあります、私は言わなければなりません。

ページを切り替えるにはpagingEnabled=YESでUIScrollViewを使用する必要がありますが、垂直にスクロールするにはpagingEnabled=NOが必要です。

2つの可能な戦略があります。どちらが機能するか、実装が簡単かわからないので、両方試してください。

最初:ネストされたUIScrollViews。率直に言って、私はこれを機能させる人をまだ見ていません。しかし、私は個人的に十分に努力していませんが、私の練習では、あなたが十分に努力すると、 IScrollViewに必要なことをさせる ができることを示しています。

したがって、戦略は、外側のスクロールビューで水平スクロールのみを処理し、内側のスクロールビューで垂直スクロールのみを処理することです。それを実現するには、UIScrollViewが内部的にどのように機能するかを知っておく必要があります。 hitTestメソッドをオーバーライドし、常に自分自身を返すため、すべてのタッチイベントはUIScrollViewに送られます。次に、touchesBegantouchesMovedなどの内部で、イベントに関心があるかどうかをチェックし、イベントを処理するか、内部コンポーネントに渡します。

タッチを処理するか転送するかを決定するために、UIScrollViewは最初にタッチしたときにタイマーを開始します。

  • 150ms以内に指を大きく動かしていない場合、イベントは内部ビューに渡されます。

  • 150ミリ秒以内に指を大きく動かした場合、スクロールが開始されます(イベントが内部ビューに渡されることはありません)。

    テーブル(スクロールビューのサブクラス)をタッチしてすぐにスクロールを開始すると、タッチした行が強調表示されないことに注意してください。

  • not 150ms以内に指を大きく動かし、UIScrollViewがイベントを内部ビューに渡し始めたが、thenスクロールを開始するのに十分なほど指を動かした場合、 UIScrollViewは、内部ビューでtouchesCancelledを呼び出し、スクロールを開始します。

    テーブルに触れ、指を少し押してからスクロールを開始すると、タッチした行が最初に強調表示されますが、その後は強調表示が解除されます。

これらのイベントシーケンスは、UIScrollViewの構成によって変更できます。

  • delaysContentTouchesがNOの場合、タイマーは使用されません。イベントはすぐに内部コントロールに移動します(ただし、指を十分に動かすとキャンセルされます)
  • cancelsTouchesがNOの場合、イベントがコントロールに送信されると、スクロールは発生しません。

alltouchesBegintouchesMovedtouchesEndedおよびtouchesCanceledイベントをCocoaTouchから受け取るのはUIScrollViewであることに注意してください(そのhitTestはそうすること)。その後、必要に応じて、必要に応じてそれらを内部ビューに転送します。

UIScrollViewのすべてを理解したので、その動作を変更できます。ユーザーがビューに触れて指を(少しでも)動かし始めると、ビューが垂直方向にスクロールを開始するように、垂直スクロールを優先したいと思うかもしれません。ただし、ユーザーが指を水平方向に十分に動かした場合は、垂直スクロールをキャンセルして水平スクロールを開始する必要があります。

外側のUIScrollViewをサブクラス化し(クラスにRemorsefulScrollViewという名前を付ける)、デフォルトの動作の代わりにすべてのイベントをすぐに内側のビューに転送し、大きな水平方向の動きが検出された場合にのみスクロールするようにします。

RemorsefulScrollViewをそのように動作させるにはどうすればよいですか?

  • 垂直スクロールを無効にし、delaysContentTouchesをNOに設定すると、ネストされたUIScrollViewsが機能するようになります。残念ながら、そうではありません。 UIScrollViewは高速モーション(無効にできない)に対して追加のフィルタリングを行うように見えるため、UIScrollViewを水平方向にしかスクロールできない場合でも、十分に速い垂直モーションを常に食い尽くします(無視します)。

    この影響は非常に大きいため、ネストされたスクロールビュー内での垂直スクロールは使用できません。 (あなたはまさにこのセットアップを持っているように見えるので、試してみてください:150msの間指を持ち、それを垂直方向に移動します-ネストされたUIScrollViewは期待どおりに動作します!)

  • つまり、UIScrollViewのコードをイベント処理に使用することはできません。 RemorsefulScrollViewの4つすべてのタッチ処理メソッドをオーバーライドし、最初に独自の処理を行う必要があります。水平スクロールを使用する場合は、イベントをsuper(UIScrollView)に転送するだけです。

  • ただし、touchesBeganをUIScrollViewに渡す必要があります。将来の水平スクロールのベース座標を記憶しておくためです(後で決定する場合はis水平スクロール)。 touchesBegan引数を保存できないため、後でtouchesをUIScrollViewに送信できません。次のtouchesMovedイベントの前に変更されるオブジェクトが含まれており、再現できません古い状態。

    したがって、touchesBeganをすぐにUIScrollViewに渡す必要がありますが、水平スクロールを決定するまで、touchesMovedイベントをそれから非表示にします。いいえtouchesMovedはスクロールしないことを意味するため、この最初のtouchesBeganは害を与えません。ただし、delaysContentTouchesをNOに設定して、追加のサプライズタイマーが干渉しないようにしてください。

    (オフトピック— UIScrollView canタッチを適切に保存し、元のtouchesBeganイベントを後で再現および転送できます。未公開のAPIを使用する不公平な利点があるため、タッチオブジェクトを複製できます。変異している。)

  • 常にtouchesBeganを転送する場合、touchesCancelledおよびtouchesEndedも転送する必要があります。ただし、UIScrollViewはtouchesEndedtouchesCancelledシーケンスをタッチクリックとして解釈し、内部ビューに転送するため、touchesBegantouchesEndedに変換する必要があります。すでに適切なイベントを自分で転送しているので、UIScrollViewに何も転送させたくないでしょう。

基本的に、ここで必要なことの擬似コードです。簡単にするために、マルチタッチイベントが発生した後、水平スクロールを許可しません。

// RemorsefulScrollView.h

@interface RemorsefulScrollView : UIScrollView {
  CGPoint _originalPoint;
  BOOL _isHorizontalScroll, _isMultitouch;
  UIView *_currentChild;
}
@end

// RemorsefulScrollView.m

// the numbers from an example in Apple docs, may need to tune them
#define kThresholdX 12.0f
#define kThresholdY 4.0f

@implementation RemorsefulScrollView

- (id)initWithFrame:(CGRect)frame {
  if (self = [super initWithFrame:frame]) {
    self.delaysContentTouches = NO;
  }
  return self;
}

- (id)initWithCoder:(NSCoder *)coder {
  if (self = [super initWithCoder:coder]) {
    self.delaysContentTouches = NO;
  }
  return self;
}

- (UIView *)honestHitTest:(CGPoint)point withEvent:(UIEvent *)event {
  UIView *result = nil;
  for (UIView *child in self.subviews)
    if ([child pointInside:point withEvent:event])
      if ((result = [child hitTest:point withEvent:event]) != nil)
        break;
  return result;
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    [super touchesBegan:touches withEvent:event]; // always forward touchesBegan -- there's no way to forward it later
    if (_isHorizontalScroll)
      return; // UIScrollView is in charge now
    if ([touches count] == [[event touchesForView:self] count]) { // initial touch
      _originalPoint = [[touches anyObject] locationInView:self];
    _currentChild = [self honestHitTest:_originalPoint withEvent:event];
    _isMultitouch = NO;
    }
  _isMultitouch |= ([[event touchesForView:self] count] > 1);
  [_currentChild touchesBegan:touches withEvent:event];
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
  if (!_isHorizontalScroll && !_isMultitouch) {
    CGPoint point = [[touches anyObject] locationInView:self];
    if (fabsf(_originalPoint.x - point.x) > kThresholdX && fabsf(_originalPoint.y - point.y) < kThresholdY) {
      _isHorizontalScroll = YES;
      [_currentChild touchesCancelled:[event touchesForView:self] withEvent:event]
    }
  }
  if (_isHorizontalScroll)
    [super touchesMoved:touches withEvent:event]; // UIScrollView only kicks in on horizontal scroll
  else
    [_currentChild touchesMoved:touches withEvent:event];
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
  if (_isHorizontalScroll)
    [super touchesEnded:touches withEvent:event];
    else {
    [super touchesCancelled:touches withEvent:event];
    [_currentChild touchesEnded:touches withEvent:event];
    }
}

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
  [super touchesCancelled:touches withEvent:event];
  if (!_isHorizontalScroll)
    [_currentChild touchesCancelled:touches withEvent:event];
}

@end

私はこれを実行したりコンパイルしたりしませんでした(そしてプレーンテキストエディタでクラス全体を入力しました)が、上記から始めてうまくいけばうまくいくでしょう。

私が見る唯一の隠れたキャッチは、UIScrollView以外の子ビューをRemorsefulScrollViewに追加すると、子がUIScrollViewのように常にタッチを処理しない場合、子に転送するタッチイベントがレスポンダーチェーンを介して戻ってくる場合があることです。防弾のRemorsefulScrollView実装は、touchesXxx再入力から保護します。

2番目の戦略:何らかの理由でUIScrollViewのネストがうまくいかなかったり、正しく動作するのが難しすぎる場合は、1つのUIScrollViewだけでうまくやっていくことができます。 、pagingEnabledデリゲートメソッドからscrollViewDidScrollプロパティをオンザフライで切り替えます。

斜めのスクロールを防ぐには、最初にscrollViewWillBeginDraggingのc​​ontentOffsetを記憶し、斜めの動きを検出した場合はscrollViewDidScroll内のcontentOffsetを確認してリセットする必要があります。もう1つの方法は、contentSizeをリセットして、ユーザーの指がどちらの方向に進むかを決定したら、一方向のスクロールのみを有効にすることです。 (UIScrollViewは、デリゲートメソッドからcontentSizeとcontentOffsetをいじるのをかなり許しているようです。)

それが機能しないか、結果がずさんなビジュアルになる場合は、touchesBegantouchesMovedなどをオーバーライドし、UIScrollViewに対角移動イベントを転送しないでください。 (ただし、この場合、ユーザーエクスペリエンスは単一方向に強制するのではなく、斜めの動きを無視する必要があるため最適ではありません。本当に冒険したい場合は、独自のUITouchのようにRevengeTouchのように書くことができます。 -Cは単純な古いCであり、Cほどダックタイプに近いものはありません;誰もオブジェクトの実際のクラスをチェックしない限り、他のクラスのように見せることができます。必要な座標を使用して、必要なタッチを合成できます。)

バックアップ戦略:TTScrollViewがあります。これは Three20ライブラリ にUIScrollViewを正常に再実装したものです。残念ながら、それはユーザーにとって非常に不自然で違和感がありません。ただし、UIScrollViewを使用するすべての試みが失敗した場合、カスタムコード化されたスクロールビューにフォールバックできます。可能な限りこれに反対することをお勧めします。 UIScrollViewを使用すると、将来のiPhone OSバージョンでどのように進化しても、ネイティブのルックアンドフィールを取得できます。

さて、この小さなエッセイは少し長すぎました。数日前にScrollingMadnessの仕事をした後、私はまだUIScrollViewゲームに夢中です。

追伸これらのいずれかが機能し、共有したいと思う場合は、andreyvit @ gmail.comで関連するコードをメールしてください。喜んでそれを私の ScrollingMadnessのトリックバッグ に含めます。

P.P.S.この小論文をScrollingMadnessのREADMEに追加します。

115

これはいつも私のために働く...

scrollView.contentSize = CGSizeMake(scrollView.frame.size.width * NumberOfPages, 1);
53
Tonetel

私のような怠け者の場合:

scrollview.directionalLockEnabledYESに設定します

- (void) scrollViewWillBeginDragging: (UIScrollView *) scrollView
{
    self.oldContentOffset = scrollView.contentOffset;
}

- (void) scrollViewDidScroll: (UIScrollView *) scrollView
{
    if (scrollView.contentOffset.x != self.oldContentOffset.x)
    {
        scrollView.pagingEnabled = YES;
        scrollView.contentOffset = CGPointMake(scrollView.contentOffset.x,
                                               self.oldContentOffset.y);
    }
    else
    {
        scrollView.pagingEnabled = NO;
    }
}

- (void) scrollViewDidEndDecelerating: (UIScrollView *) scrollView
{
    self.oldContentOffset = scrollView.contentOffset;
}
25
icompot

この問題を解決するために次の方法を使用しました。

まず、コントローラーヘッダーファイルで次の変数を定義します。

CGPoint startPos;
int     scrollDirection;

startPosは、デリゲートがscrollViewWillBeginDraggingメッセージを受け取ったときにcontentOffset値を保持します。したがって、このメソッドではこれを行います。

- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{
    startPos = scrollView.contentOffset;
    scrollDirection=0;
}

次に、これらの値を使用して、ユーザーが意図したスクロール方向をscrollViewDidScrollメッセージで決定します。

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

   if (scrollDirection==0){//we need to determine direction
       //use the difference between positions to determine the direction.
       if (abs(startPos.x-scrollView.contentOffset.x)<abs(startPos.y-scrollView.contentOffset.y)){          
          NSLog(@"Vertical Scrolling");
          scrollDirection=1;
       } else {
          NSLog(@"Horitonzal Scrolling");
          scrollDirection=2;
       }
    }
//Update scroll position of the scrollview according to detected direction.     
    if (scrollDirection==1) {
       [scrollView setContentOffset:CGPointMake(startPos.x,scrollView.contentOffset.y) animated:NO];
    } else if (scrollDirection==2){
       [scrollView setContentOffset:CGPointMake(scrollView.contentOffset.x,startPos.y) animated:NO];
    }
 }

最後に、ユーザーがドラッグを終了したときにすべての更新操作を停止する必要があります。

 - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{
    if (decelerate) {
       scrollDirection=3;
    }
 }
16
Yildiray Meric

私はここで墓掘りをしているかもしれませんが、同じ問題を解決しようとしていたので、今日この投稿に出くわしました。ここに私の解決策があり、うまく機能しているようです。

画面いっぱいのコンテンツを表示したいのですが、ユーザーが上下左右に4つの他の画面いっぱいのいずれかにスクロールできるようにします。サイズが320x480でcontentSizeが960x1440の単一のUIScrollViewがあります。コンテンツオフセットは320,480から始まります。 scrollViewDidEndDecelerating:で、5つのビュー(中央のビューとその周りの4つのビュー)を再描画し、コンテンツオフセットを320,480にリセットします。

これが私のソリューションの中核であるscrollViewDidScroll:メソッドです。

- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{   
    if (!scrollingVertically && ! scrollingHorizontally)
    {
        int xDiff = abs(scrollView.contentOffset.x - 320);
        int yDiff = abs(scrollView.contentOffset.y - 480);

        if (xDiff > yDiff)
        {
            scrollingHorizontally = YES;
        }
        else if (xDiff < yDiff)
        {
            scrollingVertically = YES;
        }
    }

    if (scrollingHorizontally)
    {
        scrollView.contentOffset = CGPointMake(scrollView.contentOffset.x, 480);
    }
    else if (scrollingVertically)
    {
        scrollView.contentOffset = CGPointMake(320, scrollView.contentOffset.y);
    }
}
13
James J

サブビューの高さをスクロールビューの高さとコンテンツサイズの高さと同じにするのは簡単です。次に、垂直スクロールが無効になります。

4
julianlubenov

ここに、直交スクロールのみを許可するページUIScrollViewの私の実装があります。必ずpagingEnabledYESに設定してください。

PagedOrthoScrollView.h

@interface PagedOrthoScrollView : UIScrollView
@end

PagedOrthoScrollView.m

#import "PagedOrthoScrollView.h"

typedef enum {
  PagedOrthoScrollViewLockDirectionNone,
  PagedOrthoScrollViewLockDirectionVertical,
  PagedOrthoScrollViewLockDirectionHorizontal
} PagedOrthoScrollViewLockDirection; 

@interface PagedOrthoScrollView ()
@property(nonatomic, assign) PagedOrthoScrollViewLockDirection dirLock;
@property(nonatomic, assign) CGFloat valueLock;
@end

@implementation PagedOrthoScrollView
@synthesize dirLock;
@synthesize valueLock;

- (id)initWithFrame:(CGRect)frame {
  self = [super initWithFrame:frame];
  if (self == nil) {
    return self;
  }

  self.dirLock = PagedOrthoScrollViewLockDirectionNone;
  self.valueLock = 0;

  return self;
}

- (void)setBounds:(CGRect)bounds {
  int mx, my;

  if (self.dirLock == PagedOrthoScrollViewLockDirectionNone) {
    // is on even page coordinates, set dir lock and lock value to closest page 
    mx = abs((int)CGRectGetMinX(bounds) % (int)CGRectGetWidth(self.bounds));
    if (mx != 0) {
      self.dirLock = PagedOrthoScrollViewLockDirectionHorizontal;
      self.valueLock = (round(CGRectGetMinY(bounds) / CGRectGetHeight(self.bounds)) *
                        CGRectGetHeight(self.bounds));
    } else {
      self.dirLock = PagedOrthoScrollViewLockDirectionVertical;
      self.valueLock = (round(CGRectGetMinX(bounds) / CGRectGetWidth(self.bounds)) *
                        CGRectGetWidth(self.bounds));
    }

    // show only possible scroll indicator
    self.showsVerticalScrollIndicator = dirLock == PagedOrthoScrollViewLockDirectionVertical;
    self.showsHorizontalScrollIndicator = dirLock == PagedOrthoScrollViewLockDirectionHorizontal;
  }

  if (self.dirLock == PagedOrthoScrollViewLockDirectionHorizontal) {
    bounds.Origin.y = self.valueLock;
  } else {
    bounds.Origin.x = self.valueLock;
  }

  mx = abs((int)CGRectGetMinX(bounds) % (int)CGRectGetWidth(self.bounds));
  my = abs((int)CGRectGetMinY(bounds) % (int)CGRectGetHeight(self.bounds));

  if (mx == 0 && my == 0) {
    // is on even page coordinates, reset lock
    self.dirLock = PagedOrthoScrollViewLockDirectionNone;
  }

  [super setBounds:bounds];
}

@end
3
Mattias Wadman

私もこの問題を解決しなければなりませんでしたが、Andrey Tarantsovの答えにはUIScrollViewsがどのように機能するかを理解するのに役立つ情報がたくさんありますが、解決策はやや複雑すぎると感じています。サブクラス化やタッチ転送を必要としない私のソリューションは次のとおりです。

  1. 各方向に1つずつ、2つのネストされたスクロールビューを作成します。
  2. 2つのダミーUIPanGestureRecognizersを作成します。これも各方向に1つです。
  3. UIGestureRecognizer's-requireGestureRecognizerToFail:セレクターを使用して、UIScrollViews 'panGestureRecognizerプロパティと、UIScrollViewの目的のスクロール方向に垂直な方向に対応するダミーUIPanGestureRecognizerの間に障害依存関係を作成します。
  4. ダミーのUIPanGestureRecognizersの-gestureRecognizerShouldBegin:コールバックで、ジェスチャの-translationInView:セレクターを使用してパンの初期方向を計算します(タッチが登録するのに十分に変換されるまで、UIPanGestureRecognizersはこのデリゲートコールバックを呼び出しません鍋として)。計算された方向に応じてジェスチャレコグナイザーの開始を許可または禁止します。これにより、垂直UIScrollViewパンジェスチャレコグナイザーの開始を許可するかどうかを制御する必要があります。
  5. -gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:では、ダミーUIPanGestureRecognizersをスクロールと並行して実行しないでください(追加したい追加の動作がない限り)。
3
Kurt Horimoto

これはicompotの改善されたソリューションであり、私にとっては非常に不安定でした。これはうまく機能し、実装は簡単です。

- (void) scrollViewWillBeginDragging: (UIScrollView *) scrollView
{
    self.oldContentOffset = scrollView.contentOffset;
}

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

    float XOffset = fabsf(self.oldContentOffset.x - scrollView.contentOffset.x );
    float YOffset = fabsf(self.oldContentOffset.y - scrollView.contentOffset.y );

        if (scrollView.contentOffset.x != self.oldContentOffset.x && (XOffset >= YOffset) )
        {

            scrollView.pagingEnabled = YES;
            scrollView.contentOffset = CGPointMake(scrollView.contentOffset.x,
                                                   self.oldContentOffset.y);

        }
        else
        {
            scrollView.pagingEnabled = NO;
               scrollView.contentOffset = CGPointMake( self.oldContentOffset.x,
                                                   scrollView.contentOffset.y);
        }

}


- (void) scrollViewDidEndDecelerating: (UIScrollView *) scrollView
{
    self.oldContentOffset = scrollView.contentOffset;
}
2

将来の参考のために、Andrey Tanasovの答えに基づいて構築し、正常に機能する次のソリューションを思い付きました。

最初にcontentOffsetを保存する変数を定義し、0,0座標に設定します

var positionScroll = CGPointMake(0, 0)

次に、scrollViewデリゲートに次を実装します。

override func scrollViewWillBeginDragging(scrollView: UIScrollView) {
    // Note that we are getting a reference to self.scrollView and not the scrollView referenced passed by the method
    // For some reason, not doing this fails to get the logic to work
    self.positionScroll = (self.scrollView?.contentOffset)!
}

override func scrollViewDidScroll(scrollView: UIScrollView) {

    // Check that contentOffset is not nil
    // We do this because the scrollViewDidScroll method is called on viewDidLoad
    if self.scrollView?.contentOffset != nil {

        // The user wants to scroll on the X axis
        if self.scrollView?.contentOffset.x > self.positionScroll.x || self.scrollView?.contentOffset.x < self.positionScroll.x {

            // Reset the Y position of the scrollView to what it was BEFORE scrolling started
            self.scrollView?.contentOffset = CGPointMake((self.scrollView?.contentOffset.x)!, self.positionScroll.y);
            self.scrollView?.pagingEnabled = true
        }

        // The user wants to scroll on the Y axis
        else {
            // Reset the X position of the scrollView to what it was BEFORE scrolling started
            self.scrollView?.contentOffset = CGPointMake(self.positionScroll.x, (self.scrollView?.contentOffset.y)!);
            self.collectionView?.pagingEnabled = false
        }

    }

}

お役に立てれば。

2
Benjamin

トネテルに感謝します。あなたのアプローチを少し変更しましたが、水平スクロールを防ぐために必要なものでした。

self.scrollView.contentSize = CGSizeMake(self.scrollView.contentSize.width, 1);
2
Jeremy

私がやったことは、ビュー画面のサイズのフレームでページングUIScrollViewを作成し、コンテンツサイズをすべてのコンテンツに必要な幅と高さ1に設定しました(Tonetelに感謝します)。次に、各ページにフレームが設定されたメニューUIScrollViewページ、ビュー画面のサイズを作成し、それぞれのコンテンツサイズを画面の幅と各ページに必要な高さに設定しました。次に、各ページをページングビューに追加しました。ページングスクロールビューではページングが有効になっているが、メニュービューでは無効になっていることを確認し(デフォルトでは無効にする必要があります)、すぐに使用できるはずです。

ページングスクロールビューは水平方向にスクロールするようになりましたが、コンテンツの高さのために垂直方向にはスクロールしません。各ページは垂直にスクロールしますが、コンテンツサイズの制約のため、水平にはスクロールしません。

その説明が望まれる何かを残した場合のコードは次のとおりです。

UIScrollView *newPagingScrollView =  [[UIScrollView alloc] initWithFrame:self.view.bounds]; 
[newPagingScrollView setPagingEnabled:YES];
[newPagingScrollView setShowsVerticalScrollIndicator:NO];
[newPagingScrollView setShowsHorizontalScrollIndicator:NO];
[newPagingScrollView setDelegate:self];
[newPagingScrollView setContentSize:CGSizeMake(self.view.bounds.size.width * NumberOfDetailPages, 1)];
[self.view addSubview:newPagingScrollView];

float pageX = 0;

for (int i = 0; i < NumberOfDetailPages; i++)
{               
    CGRect pageFrame = (CGRect) 
    {
        .Origin = CGPointMake(pageX, pagingScrollView.bounds.Origin.y), 
        .size = pagingScrollView.bounds.size
    };
    UIScrollView *newPage = [self createNewPageFromIndex:i ToPageFrame:pageFrame]; // newPage.contentSize custom set in here        
    [pagingScrollView addSubview:newPage];

    pageX += pageFrame.size.width;  
}           
2
Jon Conner

ドキュメントから:

「デフォルト値はNOです。これは、水平方向と垂直方向の両方でスクロールが許可されていることを意味します。 」

重要な部分は、「ユーザーがbegins 1つの一般的な方向にドラッグする場合」だと思います。そのため、斜めにドラッグし始めても、これは機能しません。これらのドキュメントは、解釈に関しては常に信頼できるというわけではありません。

これは実際には賢明なようです。私は尋ねなければならない-なぜあなたはいつでも水平のみまたは垂直のみに制限したいのですか?おそらく、UIScrollViewはあなたのためのツールではありませんか?

1
philsquared

touchesEndedおよびtouchesCancelled_isHorizontalScrollをNOにリセットする必要があります。

1
Praveen Matanam

私のビューは10個の水平ビューで構成されており、それらのビューのいくつかは複数の垂直ビューで構成されているため、次のようになります。


1.0   2.0   3.1    4.1
      2.1   3.1
      2.2
      2.3

水平軸と垂直軸の両方でページングが有効になっている

ビューには次の属性もあります。

[self setDelaysContentTouches:NO];
[self setMultipleTouchEnabled:NO];
[self setDirectionalLockEnabled:YES];

斜めスクロールを防ぐには、次のようにします。

-(void)scrollViewDidScroll:(UIScrollView *)scrollView {
    int pageWidth = 768;
    int pageHeight = 1024;
    int subPage =round(self.contentOffset.y / pageHeight);
    if ((int)self.contentOffset.x % pageWidth != 0 && (int)self.contentOffset.y % pageHeight != 0) {
        [self setContentOffset:CGPointMake(self.contentOffset.x, subPage * pageHeight];
    } 
}

私にとって魅力のように動作します!

1
user422756

大きなスクロールビューを使用していて、斜めスクロールを制限する場合 http://chandanshetty01.blogspot.in/2012/07/restricting-diagonal-scrolling-in.html

1

Swiftで@AndreyTarantsovの2番目のソリューションを探している人は、コードで次のようになります。

    var positionScroll:CGFloat = 0

    func scrollViewWillBeginDragging(scrollView: UIScrollView) {
        positionScroll = self.theScroll.contentOffset.x
    }

    func scrollViewDidScroll(scrollView: UIScrollView) {
        if self.theScroll.contentOffset.x > self.positionScroll || self.theScroll.contentOffset.x < self.positionScroll{
             self.theScroll.pagingEnabled = true
        }else{
            self.theScroll.pagingEnabled = false
        }
    }

ここで行っているのは、scrollviewがドラッグされる直前に現在の位置を保持し、xがxの現在の位置と比較して増加または減少しているかどうかを確認することです。はいの場合、pagingEnabledをtrueに設定し、いいえの場合(yが増加/減少)、pagingEnabledをfalseに設定します。

それが新規参入者に役立つことを願っています!

0
jonprasetyo

3.0ベータでこれを試したことがない場合は、試してみることをお勧めします。現在、スクロールビュー内で一連のテーブルビューを使用していますが、期待どおりに機能します。 (まあ、とにかくスクロールはします。まったく異なる問題の修正を探しているときにこの質問を見つけました...)

0
Tim Keating