web-dev-qa-db-ja.com

UITableViewで水平パンを検出する

UIPanGestureRecognizerを使用して、UITableViewの水平方向のスライドを認識しています(正確にはセル上ですが、テーブル自体に追加されています)。ただし、このジェスチャレコグナイザは明らかにテーブルからタッチを盗みます。私はすでにpangesturerecognizerに水平方向のスライドを認識させ、それにスナップさせました。ただし、ユーザーが垂直方向にスライドすることから始める場合は、そのタッチからテーブルビューにすべてのイベントを渡す必要があります。

私が試したことの1つは、レコグナイザーを無効にすることでしたが、次のタッチイベントまでスクロールしませんでした。だから私はすぐにイベントを通過するためにそれが必要になるでしょう。

私が試したもう1つのことは、自分でスクロールさせることでしたが、タッチを停止した後、持続的な速度を逃してしまいます。

ここにいくつかのコードがあります:

//In the viewdidload method
UIPanGestureRecognizer *slideRecognizer = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(sliding:)];
[myTable addGestureRecognizer:slideRecognizer];



-(void)sliding:(UIPanGestureRecognizer *)recognizer
{
    if (recognizer.state == UIGestureRecognizerStateBegan)
    {
    CGPoint translation = [recognizer translationInView:favoritesTable];
    if (sqrt(translation.x*translation.x)/sqrt(translation.y*translation.y)>1) {
        horizontalScrolling = YES; //BOOL declared in the header file
        NSLog(@"horizontal");
        //And some code to determine what cell is being scrolled:
        CGPoint slideLocation = [recognizer locationInView:myTable];
        slidingCell = [myTable indexPathForRowAtPoint:slideLocation];
        if (slidingCell.row == 0) {
            slidingCell = nil;
        }

    }
    else
    {
        NSLog(@"cancel");
    }

    if (recognizer.state == UIGestureRecognizerStateEnded || recognizer.state == UIGestureRecognizerStateCancelled)
    {
    horizontalScrolling = NO;
    }


    if (horizontalScrolling)
    {
        //Perform some code
    }
    else
    {
    //Maybe pass the touch from here; It's panning vertically
    }

}

だから、タッチを渡す方法について何かアドバイスはありますか?

追加:テーブルビューのジェスチャレコグナイザーメソッドをサブクラス化して、最初に水平かどうかを確認することも考えました。ただし、元のコードが必要になると思います... Appleで問題が発生するかどうかはわかりません。また、UITableView(controller)をサブクラス化せず、セルのみをサブクラス化しました。 。このコードは、テーブルを保持するビューコントローラにあります;)

31
Erik S

私は同じ問題を抱えていて、UIPanGestureRecognizerで動作する解決策を思いつきました。

Erikとは対照的に、パンをサポートするために一度に1つの特定のセルが必要なため、セルにUIPanGestureRecognizerを直接追加しました。しかし、これはエリックの場合にもうまくいくはずだと思います。

これがコードです。

- (BOOL)gestureRecognizerShouldBegin:(UIPanGestureRecognizer *)gestureRecognizer
{
    UIView *cell = [gestureRecognizer view];
    CGPoint translation = [gestureRecognizer translationInView:[cell superview]];

    // Check for horizontal gesture
    if (fabsf(translation.x) > fabsf(translation.y))
    {
        return YES;
    }

    return NO;
}

水平ジェスチャの計算は、Erikのコードからコピーされます。これはiOS4.3でテストしました。

編集:この実装により、「スワイプして削除」ジェスチャが防止されることがわかりました。その動作を取り戻すために、上記のifステートメントにジェスチャの速度のチェックを追加しました。

if ([gestureRecognizer velocityInView:cell].x < 600 && sqrt(translate...

デバイスで少し遊んだ後、500から600の速度を思いつきました。これは、パンとスワイプして削除するジェスチャの間の移行に最適なユーザーエクスペリエンスを提供すると思います。

56
Florian Mielke

私の答えはFlorianMielkeの答えと同じですが、いくつか簡略化して修正しました。

使い方:

UIPanGestureRecognizerにデリゲート(UIGestureRecognizerDelegate)を渡すだけです。例えば:

UIPanGestureRecognizer *panner = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panDetected:)];
panner.delegate = self;
[self addGestureRecognizer:panner];

次に、そのデリゲートに次のメソッドを実装させます。

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
    CGPoint translation = [(UIPanGestureRecognizer *)gestureRecognizer translationInView:gestureRecognizer.view.superview];
    return fabsf(translation.x) > fabsf(translation.y);
}
6
Thane Brimhall

ジェスチャレコグナイザの代わりに、タッチイベントを手動で使用してみてください。スワイプジェスチャを最終的に認識した場合を除いて、常にイベントをテーブルビューに戻します。

UIResponderから継承するすべてのクラスには、4つのタッチ機能(開始、終了、キャンセル、および移動)があります。したがって、呼び出しを「転送」する最も簡単な方法は、クラスで呼び出しを処理してから、処理する次のオブジェクトで明示的に呼び出すことです(ただし、オブジェクトが最初にメッセージに応答するかどうかを確認する必要があります。 responsesToSelector:オプションの関数であるため)。このようにして、必要なイベントを検出し、他の要素が必要とする通常のタッチ操作を可能にすることができます。

1
drewag

ヒントをありがとう!最終的にUITableViewサブクラスに移動し、動きが水平であるかどうかを確認し(この場合、カスタム動作を使用します)、それ以外の場合は[super touchesMoved:withEvent:];を呼び出します。

しかし、なぜこれが機能するのかまだよくわかりません。確認したところ、superはUITableViewです。この階層がどのように機能するかをまだ完全には理解していないようです。誰かが説明してみることができますか?

0
Erik S

代わりにUISwipeGestureRecognizerを使用できるかもしれませんか? directionプロパティを使用して、上下のスワイプを無視するように指示できます。

0
Kobski