如何让UILongPressGestureRecognizer仅响应父视图长按事件(子视图需保留交互)
让UILongPressGestureRecognizer仅响应父视图触摸的解决方案
嘿,这个问题我之前也碰到过——默认情况下给父视图添加的手势识别器,会把落在子视图上的触摸也当成父视图的触摸来处理。你现在代码里的sender?.view == self.view判断其实起不到作用,因为手势是绑定在父视图上的,不管触摸点在子视图还是父视图本身,sender?.view永远是父视图。
下面给你两种靠谱的解决办法,都不需要禁用子视图的交互,完全满足你的需求:
方法一:用手势代理过滤触摸(推荐)
通过实现UIGestureRecognizerDelegate的代理方法,我们可以直接从源头过滤掉子视图上的触摸事件,不让手势处理这些无效触发:
- 首先让你的ViewController遵守代理协议:
class ExampleViewController: UIViewController, UIGestureRecognizerDelegate { // 原有代码保持不变 }
- 给手势设置代理,并实现过滤方法:
override func viewDidLoad() { super.viewDidLoad() let smallView: UIView = { let view = UIView(frame: CGRect(x: 30, y: 30, width: 100, height: 100)) view.backgroundColor = .red return view }() view.addSubview(smallView) let longPressMultiSelection = UILongPressGestureRecognizer(target: self, action: #selector(handleLongPress(_:))) longPressMultiSelection.minimumPressDuration = 0.5 // 关键步骤:设置手势的代理 longPressMultiSelection.delegate = self view.addGestureRecognizer(longPressMultiSelection) } // 代理方法:判断手势是否需要接收当前触摸事件 func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool { // 只有当触摸的视图是父视图本身时,才允许手势处理 return touch.view == self.view } @objc func handleLongPress(_ sender: UILongPressGestureRecognizer? = nil) { // 这里不需要再额外判断视图,代理已经帮我们过滤好了无效触摸 switch sender?.state { case .began, .changed: print("here") case .ended, .cancelled, .failed: print("ended") default: print("error") } }
这个方法的优势是逻辑清晰,后续即使添加更多子视图,也不需要修改手势处理逻辑,维护成本很低。
方法二:在处理方法里判断触摸位置
如果你不想用代理,也可以在手势触发后,检查触摸点是否落在父视图的空白区域(不包含子视图):
@objc func handleLongPress(_ sender: UILongPressGestureRecognizer? = nil) { guard let gesture = sender else { return } // 获取触摸点在父视图坐标系中的位置 let touchPoint = gesture.location(in: self.view) // 找到目标子视图(这里用背景色判断,你也可以给子视图加tag或者存成实例属性) if let smallView = view.subviews.first(where: { $0.backgroundColor == .red }) { // 如果触摸点在子视图范围内,直接返回不执行后续逻辑 if smallView.frame.contains(touchPoint) { return } } // 只有触摸在父视图空白区域才执行逻辑 switch gesture.state { case .began, .changed: print("here") case .ended, .cancelled, .failed: print("ended") default: print("error") } }
这种方法适合子视图数量少、位置固定的场景,但如果后续子视图动态变化或者数量增多,维护起来会比较麻烦,所以更推荐第一种代理方法。
两种方法都能实现你的核心需求:长按父视图空白区域触发手势,长按子视图不会触发,同时子视图的点击等交互功能不受任何影响。
内容的提问来源于stack exchange,提问作者Bluebyte
相关产品推荐
相关产品推荐

