为什么 UITapGestureRecognizer 永远不会在状态开始时被调用?

问题描述

如果您将 UITapGestureRecognizer 分配给 UIView,当用户触摸视图时,UIGestureRecognizerStateBegan 不会出现。

// Tap

_tapGestureRecognizer =
[[UITapGestureRecognizer alloc] initWithTarget:self
                                            action:@selector(tap:)];
    
[_someView addGestureRecognizer:_tapGestureRecognizer];

相反,当用户点击时,识别器会直接跳转UIGestureRecognizerStateEnded

我必须将该视图更改为 UIButton 并听取 touchDown 方法

_someButton = [[UIButton alloc] init];
    
[_someButton addTarget:self action:@selector(touchDown:) forControlEvents:UIControlEventTouchDown];
    
[self addSubview: _someButton];

我不喜欢仅仅为此将 UIView 更改为 UIButton

我可以改用 UITapGestureRecognizer 吗?

解决方法

首先让我说 UITapGestureRecognizer 文档清楚地告诉期待所有状态的回调。

对于手势识别,指定数量的手指必须点击视图指定次数。 虽然点击是离散的手势,但它们对于手势识别器的每个状态都是离散的。系统在手势开始时发送相关的动作消息,然后针对每个中间状态再次发送相关的动作消息,直到(并包括)手势的结束状态。 处理点击手势的代码应该测试手势的状态,例如:

func handleTap(sender: UITapGestureRecognizer) {
    if sender.state == .ended {
        // handling code
    }
}

但是它几乎没有意义(特别是在单击识别器的情况下)。您触摸了一个视图(添加了点击手势),您还没有抬起手指,移动它等等。系统无法在 .touchDown 事件发生时知道此交互将转向成功识别点击(需要抬起手指)。

本质上,UITapGestureRecognizer(用于单点触摸)是一个 .touchDown + .touchUp 组合。如果在 .touchDown 之后发生任何其他事情,例如拖动(.touchDragInside OR .touchDragExit),可能会导致成功识别平移手势(tableView 滚动等)

您可以将 UITapGestureRecognizer 大致等价于到按钮的 .touchUpInside 事件。按钮的 .touchUpInside 事件不会为 .touchDown 事件调用您的函数,只有通过明确要求相同的事件才能接收该事件。

为什么文档这么说?

也许系统能够识别其他场景的 .began 状态

  1. 多次点击手势 - 双击/三次点击(参见 UITapGestureReconizer.numberOfTapsRequired

  2. 多点触控 - 2/3 手指轻点(参见 UITapGestureReconizer.numberOfTouchesRequired

如果您想了解更多信息,您必须为此测试其他场景。