设置delaysTouchesBegan为true的手势识别器会中断touchesMoved调用吗?
嘿,我来帮你捋清楚这个问题——你遇到的情况其实是UIGestureRecognizer的delaysTouchesBegan属性的预期行为,但和你预想的逻辑有点不一样,咱们一步步拆解:
为什么会出现这个现象?
当你把捏合手势识别器的delaysTouchesBegan设为true时,系统会暂时把触摸事件扣下来,不立刻传给你的视图控制器。这么做是为了让手势识别器先判断:这个触摸序列是不是它要处理的手势(毕竟捏合需要两根手指)。
但这里有个关键细节:当手势识别器确定这不是它要处理的手势(比如你只是单指拖动),它不会补发之前扣下的那些touchesMoved事件——它只会补发touchesBegan,然后后续的触摸事件才会正常传递。这就导致你只看到了touchesBegan,中间拖动产生的touchesMoved都被丢弃了,最后可能只会收到touchesEnded或者touchesCancelled(你描述里的“touches...的内容”应该是指这个吧?)
几个可行的解决办法
根据你的业务场景,这里有几个靠谱的方案:
1. 直接关闭delaysTouchesBegan
如果你的场景允许,把这个属性设为false就完事了。这样触摸事件会立刻传给视图控制器,手势识别器同时并行识别。不过要注意,这可能会导致手势和触摸事件的冲突(比如用户本来想捏合,结果先触发了拖动),需要根据你的需求权衡。
2. 自定义捏合手势识别器,提前放弃单指识别
默认的捏合手势会等一会儿确认有没有第二根手指,你可以自定义一个子类,看到单指触摸就直接放弃识别,让触摸事件正常传递:
class CustomPinchGestureRecognizer: UIPinchGestureRecognizer { override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) { super.touchesBegan(touches, with: event) // 单指触摸直接标记为失败,不拦截后续事件 if touches.count == 1 { state = .failed } } }
这样用户单指拖动时,手势识别器会立刻进入.failed状态,不会再扣住触摸事件,你的控制器就能收到完整的touchesBegan→touchesMoved→touchesEnded序列了。
3. 用代理方法精准控制延迟逻辑
实现UIGestureRecognizerDelegate的gestureRecognizer(_:shouldDelayTouchesBegan:)方法,针对单指触摸的情况,不让手势识别器延迟事件:
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldDelayTouchesBegan shouldDelay: Bool) -> Bool { guard let touches = gestureRecognizer.touches(for: gestureRecognizer.view ?? UIView()) else { return shouldDelay } // 只有当触摸数大于1(捏合的前提)时,才延迟触摸事件 return gestureRecognizer is UIPinchGestureRecognizer && touches.count > 1 }
这个方法会告诉系统:只有当用户用两根手指触摸时,才触发延迟逻辑;单指触摸时直接把事件传给控制器,完美兼顾捏合手势和单指拖动的需求。
总结
核心问题就是delaysTouchesBegan的拦截逻辑——它不会补发已经扣下的中间触摸事件。上面的方案里,自定义手势或者代理方法是比较优雅的选择,既能保留捏合手势的功能,又不影响单指触摸的正常事件流。
内容的提问来源于stack exchange,提问作者Rob N

