【问题标题】:How to detect double-taps on cells in a UICollectionView如何检测 UICollectionView 中单元格上的双击
【发布时间】:2012-09-29 08:22:59
【问题描述】:

我想响应在 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's documentation 在这一点上具有误导性,声称默认手势识别器是 UITapGestureRecognizer 子类的一个实例,因此可以使用[recogniser isKindOfClass:[UITapGestureRecognizer class]] 轻松识别。不幸的是,这是一个错误。

【问题讨论】:

  • 在我的收藏视图中添加点击识别器时,我没有看到任何冲突——它可以很好地响应双击(它也响应收藏视图的单击识别器)。
  • UICollectionViewDelegate 协议的 collectionView:didSelectItemAtIndexPath: 回调不起作用(在我的测试中)添加了点击识别器。
  • @rdelmar:感谢您的测试。它现在也适用于我(不确定我最初的测试中发生了什么)。因此,我大大改变了问题。

标签: ios objective-c uicollectionview uikit uitapgesturerecognizer


【解决方案1】:

对于寻求快速答案的读者,这是@RegularExpression 和@Edwin Iskandar 答案的组合。

在您持有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)
    }
  }

【讨论】:

    【解决方案2】:

    我不明白你为什么需要 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;
    

    这消除了单击单元格选择的干扰。这提供了更强大的设置。

    【讨论】:

    • 这一切都与我不再从事的项目中的代码有关,因此我无法查看。但是 IIRC 我试图阻止collectionView:didSelectItemAtIndexPath: 在识别到双击时被调用。我希望这两个手势是互斥的。碰巧的是,应用程序的设计发生了变化,让这一切变得无关紧要,所以我从来没有解决这个问题。
    • 我使用的是相同的代码,它工作正常,但有一个例外。如果集合视图滚动,则 locationInView 不会与滚动位置对齐。有什么办法解决吗?
    【解决方案3】:

    这里有很多很好的解决方案,但不幸的是它们对我来说并不可靠(例如,我无法让双击持续触发,可能是因为我还实现了 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
    }
    

    【讨论】:

    • 这应该是公认的答案。像魅力一样工作。
    • 是的,这是最好的方法。每个 UICollectionView 只有一个 UITapGestureRecognizer 实例,而不是很多很多...
    • 将@RegularExpression 答案(doubleTapGesture.delaysTouchesBegan = true)与这个结合起来是 IMO 最好的解决方案之一。您可以消除简单点击和双击之间的干扰,同时保持简单的方法。
    【解决方案4】:

    我的解决方案是不实现 collectionView:didSelectItemAtIndexPath 而是实现两个手势识别器。

        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 上选择了单元格,但如果这让您感到困扰,您可以在两个选择器中处理它。

    【讨论】:

    • 这就是我所追求的方式,它对我来说效果很好。不要忘记删除 collectionView:didSelectItemAtIndexPath: 如果有的话。
    【解决方案5】:

    在默认手势识别器上调用的requireGestureRecognizerToFail: 确实有效(也就是说,如果识别到双击,它们的状态将变为 UIGestureRecognizerStateFailed)。

    但似乎 UICollectionView 的 collectionView:didSelectItemAtIndexPath: 委托回调没有考虑到这一点,即。当默认手势识别器失败时仍然会调用它。

    因此,答案/解决方法是确保代理的 collectionView:shouldSelectItemAtIndexPath:collectionView:shouldDeselectItemAtIndexPath: 实现检查(其中一个?)默认手势识别器的状态,因此:

    - (BOOL)collectionView:(UICollectionView *)collectionView shouldSelectItemAtIndexPath:(NSIndexPath *)indexPath {
    
        UITapGestureRecognizer *defaultGestureRecogniser = [[self.collectionView gestureRecognizers] objectAtIndex:0];
        return defaultGestureRecogniser.state != UIGestureRecognizerStateFailed;
    }
    

    【讨论】:

    • 事实证明这并不完全正确。默认手势识别器在进入选择委托方法时总是处于失败状态,除非使用较长的点击。因此,不能使用快速单击来选择单元格。
    猜你喜欢
    • 1970-01-01
    • 2012-09-12
    • 2010-11-05
    • 2018-08-30
    • 2014-07-22
    • 1970-01-01
    • 2018-04-16
    • 2021-09-23
    • 1970-01-01
    相关资源
    最近更新 更多