【问题标题】:Add double tap to UICollectionView; require single tap to fail双击 UICollectionView;需要单击才能失败
【发布时间】:2013-07-16 08:22:38
【问题描述】:

遇到与this question 类似的问题,我正在尝试将双击手势识别器添加到我的UICollectionView 实例中。

我需要防止默认单击调用UICollectionViewDelegate方法collectionView:didSelectItemAtIndexPath:

为了实现这一点,我实现了代码straight from Apple's Collection View Programming Guide (Listing 4-2)

UITapGestureRecognizer* tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapGesture:)];
NSArray* recognizers = [self.collectionView gestureRecognizers];

// Make the default gesture recognizer wait until the custom one fails.
for (UIGestureRecognizer* aRecognizer in recognizers) {
   if ([aRecognizer isKindOfClass:[UITapGestureRecognizer class]])
      [aRecognizer requireGestureRecognizerToFail:tapGesture];
}

// Now add the gesture recognizer to the collection view.
tapGesture.numberOfTapsRequired = 2;
[self.collectionView addGestureRecognizer:tapGesture];

此代码未按预期工作:tapGesture 在双击时触发,但未阻止默认单击,并且仍调用委托的 didSelect... 方法。

在调试器中单步执行会发现 if 条件 [aRecognizer isKindOfClass:[UITapGestureRecognizer class]] 永远不会评估为真,因此新的 tapGesture 上的故障要求未建立。

每次通过for循环运行这个调试器命令:

po (void)NSLog(@"%@",(NSString *)NSStringFromClass([aRecognizer class]))

显示默认手势识别器(确实)不是UITapGestureRecognizer 实例。

相反,它们是私有类 UIScrollViewDelayedTouchesBeganGestureRecognizerUIScrollViewPanGestureRecognizer

首先,我不能在不违反私有 API 规则的情况下明确使用这些。其次,通过requireGestureRecognizerToFail: 附加到UIScrollViewDelayedTouchesBeganGestureRecognizer 似乎无论如何都不能提供所需的行为——即仍然调用代理的didSelect...

如何使用UICollectionView 的默认手势识别器向集合视图添加双击并防止默认单击也触发委托的collectionView:didSelectItemAtIndexPath: 方法?

提前致谢!

【问题讨论】:

  • 实现collectionView:didSelectItemAtIndexPath:还不够吗?
  • @cahn:不用担心——除了标准行为之外,我还需要双击。
  • This question 似乎解决了您遇到的同样问题。有用吗?
  • @AshFurrow:恐怕不行——我的双击工作正常,但我(也)不能停止 didSelect... 也被调用了。关于 shouldSelect... 和 shouldDeselect... 的建议看起来很有趣,但“呃...,这不对”的评论让我有点不确定如何继续。

标签: uigesturerecognizer uicollectionview uitapgesturerecognizer


【解决方案1】:

我的解决方案是不实现 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 上选择了单元格,但如果这让您感到困扰,您可以在两个选择器中处理它。

【讨论】:

  • 感谢您的回答。看来这是要走的路。
  • 我不推荐这个解决方案,因为以下原因:1)您可能有现有的代码,已经使用了选择 2)您将来可能想要选择 3)手势识别器旨在用于任何视图和此视图可以具有用于处理触摸事件的所有不同逻辑。使用 delaysTouchesBegan/Ended 是更具可扩展性的解决方案。 (见 Szilto 的回答)
  • 我现在同意,但早在 13 年,我很确定我尝试过,但从未成功过。显然我可能错了,但我也喜欢 Sz'z 代码。
  • 只有在快速选择的情况下,才能使用单击手势代替 collectionView:didSelectItemAtIndexPath。如果用户触摸单元格一段时间然后松开手指,则会调用 didSelectItemAtIndexPath 但不会调用您的点击手势,因此可能会使某些用户感到沮丧
【解决方案2】:

我使用以下方法注册一个 UITapGestureRecognizer:

UITapGestureRecognizer* singleTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleSingleTapGesture:)];
singleTapGesture.delaysTouchesBegan = YES;
singleTapGesture.numberOfTapsRequired = 1; // number of taps required
singleTapGesture.numberOfTouchesRequired = 1; // number of finger touches required
[self.collectionView addGestureRecognizer:singleTapGesture];

通过将delaysTouchesBegan 设置为YES,自定义手势识别器通过延迟注册其他触摸事件来获得高于默认集合视图点击侦听器的优先级。或者,您可以通过将cancelsTouchesInView 设置为YES 来完全取消触摸识别。

手势由以下函数处理:

- (void)handleSingleTapGesture:(UITapGestureRecognizer *)sender {

    if (sender.state == UIGestureRecognizerStateEnded) {
        CGPoint location = [sender locationInView:self.collectionsView];
        NSIndexPath *indexPath = [self.collectionsView indexPathForItemAtPoint:location];

        if (indexPath) {
            NSLog(@"Cell view was tapped.");
            UICollectionViewCell *cell = [self.collectionsView cellForItemAtIndexPath:indexPath];
            // Do something.                
        }
    }
    else{
        // Handle other UIGestureRecognizerState's
    }
}

【讨论】:

  • 在不改变现有代码的情况下完美运行,.delyaTouchesBegan 是要走的路。
猜你喜欢
  • 2017-05-31
  • 1970-01-01
  • 1970-01-01
  • 2014-01-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多