web-dev-qa-db-ja.com

UICollectionViewのセルのダブルタップを検出する方法

UICollectionViewのセルのダブルタップに応答し、ダブルタップアクションでセルの選択をキャンセルしたい。

これは私が試したものです:

UITapGestureRecognizer *tapRecogniser = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapGesture:)];
tapRecogniser.numberOfTapsRequired = 2;

 for (UITapGestureRecognizer *recogniser in [self.collectionView gestureRecognizers]) {
    [recogniser requireGestureRecognizerToFail:tapRecogniser];
}

[self.collectionView addGestureRecognizer:tapRecogniser];

つまり、ダブルタップジェスチャ認識機能が成功した場合にデフォルトのジェスチャ認識機能が失敗するようにしようとしています。

コレクションビューデリゲートのcollectionView:didSelectItemAtIndexPath:がダブルタップ後も呼び出されているため、これは機能していないようです。


AppleのUICollectionViewControllerドキュメントに関する注意

Appleのドキュメント はこの点で誤解を招き、デフォルトのジェスチャ認識機能はUITapGestureRecognizerサブクラスのインスタンスであると主張しているため、[recogniser isKindOfClass:[UITapGestureRecognizer class]]で簡単に選択できます。残念ながら、これはエラーです。

28
Cris

RequireToFailが必要な理由がわかりません。 UICollectionViewでダブルタップを使用していますが、シングルタップ(選択に使用)に干渉しません。

私は以下を使用します:

UITapGestureRecognizer *doubleTapFolderGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(processDoubleTap:)];
[doubleTapFolderGesture setNumberOfTapsRequired:2];
[doubleTapFolderGesture setNumberOfTouchesRequired:1];
[self.view addGestureRecognizer:doubleTapFolderGesture];

次に、これ:

- (void) processDoubleTap:(UITapGestureRecognizer *)sender
{
    if (sender.state == UIGestureRecognizerStateEnded)
    {
        CGPoint point = [sender locationInView:collectionView];
        NSIndexPath *indexPath = [collectionView indexPathForItemAtPoint:point];
        if (indexPath)
        {
            NSLog(@"Image was double tapped");
        }
        else 
        {
            DoSomeOtherStuffHereThatIsntRelated;
        }
    }
}

正常に動作しているようです。ダブルタップが認識され、必要に応じて処理できます(この場合、フォルダーの内容を展開しています)。ただし、シングルタップすると、タップされた売りが選択されます。これについては、ジェスチャ認識については記述していません。

重要な編集:

特定の状況では元の回答が間違っている可能性があり、機能しているように見える明らかな修正があるため、この質問を再検討しています。

次の行を追加する必要があります。

doubleTapFolderGesture.delaysTouchesBegan = YES;

これにより、セル選択のためのシングルタップとの干渉がなくなります。これにより、はるかに堅牢なセットアップが提供されます。

45

ここにはたくさんの良い解決策がありますが、残念ながらそれらは私にとって確実に機能しませんでした(たとえば、didSelectItemAtIndexPathも実装されていたため、ダブルタップを一貫してトリガーできませんでした)。

私にとってうまくいったのは、セルの代わりに(ダブル)タップジェスチャレコグナイザーをコレクションビューに追加することでした。そのアクションセレクターで、どのセルがダブルタップされたかを判断し、必要なことは何でもします。うまくいけば、これは誰かを助けます:

- (void)viewDidLoad
{
    UITapGestureRecognizer *doubleTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(didDoubleTapCollectionView:)];
    doubleTapGesture.numberOfTapsRequired = 2;
    [self.collectionView addGestureRecognizer:doubleTapGesture];
}

- (void)didDoubleTapCollectionView:(UITapGestureRecognizer *)gesture {

    CGPoint pointInCollectionView = [gesture locationInView:self.collectionView];
    NSIndexPath *selectedIndexPath = [self.collectionView indexPathForItemAtPoint:pointInCollectionView];
    UICollectionViewCell *selectedCell = [self.collectionView cellForItemAtIndexPath:selectedIndexPath];

    // do something
}
11
Edwin Iskandar

私の解決策は、collectionView:didSelectItemAtIndexPathを実装するのではなく、2つのジェスチャレコグナイザーを実装することでした。

    self.doubleTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(processDoubleTap:)];
    [_doubleTapGesture setNumberOfTapsRequired:2];
    [_doubleTapGesture setNumberOfTouchesRequired:1];   

    [self.view addGestureRecognizer:_doubleTapGesture];

    self.singleTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(processSingleTap:)];
    [_singleTapGesture setNumberOfTapsRequired:1];
    [_singleTapGesture setNumberOfTouchesRequired:1];
    [_singleTapGesture requireGestureRecognizerToFail:_doubleTapGesture];

    [self.view addGestureRecognizer:_singleTapGesture];

このようにして、シングルタップとダブルタップを処理できます。私が見ることができる唯一の落とし穴は、セルがdoubleTapsで選択されていることですが、これが気になる場合は、2つのセレクターで処理できます。

5
Paul Cezanne

デフォルトのジェスチャ認識機能で呼び出されたrequireGestureRecognizerToFail:は実際に機能します(つまり、ダブルタップが認識された場合、それらの状態はUIGestureRecognizerStateFailedになります)。

しかし、UICollectionViewのcollectionView:didSelectItemAtIndexPath:デリゲートコールバックはこれを考慮していないようです。デフォルトのジェスチャ認識機能が失敗した場合でも呼び出されます。

したがって、答え/回避策は、デリゲートのcollectionView:shouldSelectItemAtIndexPath:およびcollectionView:shouldDeselectItemAtIndexPath:実装がデフォルトのジェスチャ認識機能(の1つ)の状態をチェックすることを確認することです。

- (BOOL)collectionView:(UICollectionView *)collectionView shouldSelectItemAtIndexPath:(NSIndexPath *)indexPath {

    UITapGestureRecognizer *defaultGestureRecogniser = [[self.collectionView gestureRecognizers] objectAtIndex:0];
    return defaultGestureRecogniser.state != UIGestureRecognizerStateFailed;
}
3
Cris

Swiftの回答を探している読者にとって、これは@RegularExpressionと@EdwinIskandarの回答を組み合わせたものです。

collectionViewを保持しているコントローラーに、次の行を追加します。


  private var doubleTapGesture: UITapGestureRecognizer!
  func setUpDoubleTap() {
    doubleTapGesture = UITapGestureRecognizer(target: self, action: #selector(didDoubleTapCollectionView))
    doubleTapGesture.numberOfTapsRequired = 2
    collectionView.addGestureRecognizer(doubleTapGesture)

    // This line delay the single touch message to the collection view.
    // Simple touch will be sent only when the double tap recogniser is sure
    // this is a simple tap.
    // You can remove it if you don't mind having both a simple tap and double
    // tap event.
    doubleTapGesture.delaysTouchesBegan = true  
  }

  @objc func didDoubleTapCollectionView() {
    let pointInCollectionView = doubleTapGesture.location(in: collectionView)
    if let selectedIndexPath = collectionView.indexPathForItem(at: pointInCollectionView) {
      let selectedCell = collectionView.cellForItem(at: selectedIndexPath)

      // Print double tapped cell's path
      print(selectedCell)
    }
  }
0
Jeremy Cochoy