自定义UIGestureRecognizer后按钮失效,如何实现视图与按钮事件共存?
嘿,我帮你搞定这个手势和按钮冲突的问题~
问题根源分析
你的自定义SingleTouchDownGestureRecognizer目前有两个核心问题:
- 默认
cancelsTouchesInView = true,当手势被标记为.recognized后,系统会直接拦截后续的触摸事件(比如touchesEnded),导致按钮收不到完整的触摸流程,自然触发不了Touch Up Inside事件。 - 没有判断触摸区域是否属于子控件(按钮),导致按钮上的触摸也会触发视图的手势响应,不符合你“触摸按钮时视图无需响应”的需求。
解决方案&代码修改
我们从两个方向调整代码就能解决问题:
1. 允许触摸事件继续传递给子控件
在添加手势时,把cancelsTouchesInView设为false,这样即使视图的手势被识别,后续的触摸事件依然能传递给按钮:
let t = SingleTouchDownGestureRecognizer(target: self, action: #selector(handleTap(sender:))) t.cancelsTouchesInView = false // 关键设置! self.view.addGestureRecognizer(t)
2. 在自定义手势中过滤按钮区域的触摸
在touchesBegan方法里,先判断触摸的目标视图是否是UIControl(按钮是它的子类),如果是直接将手势状态设为.failed,让事件正常传递给按钮,同时视图的手势不响应:
class SingleTouchDownGestureRecognizer: UIGestureRecognizer { override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) { guard let touch = touches.first else { state = .failed return } // 触摸的是按钮等UIControl控件,直接让手势失败 if let _ = touch.view as? UIControl { state = .failed return } if state == .possible { state = .recognized } } override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) { state = .failed } override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) { state = .failed } }
完整修改后的代码
import UIKit import UIKit.UIGestureRecognizerSubclass class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() let t = SingleTouchDownGestureRecognizer(target: self, action: #selector(handleTap(sender:))) t.cancelsTouchesInView = false // 允许后续触摸事件传递给子控件 self.view.addGestureRecognizer(t) } @objc func handleTap(sender: UITapGestureRecognizer? = nil) { self.view.backgroundColor = #colorLiteral(red: 0.2392156869, green: 0.6745098233, blue: 0.9686274529, alpha: 1) UIView.animate(withDuration: 1) { self.view.backgroundColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1) } } @IBAction func testBtnPressed(_ sender: Any) { print("testBtnPressed!") } } class SingleTouchDownGestureRecognizer: UIGestureRecognizer { override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) { guard let touch = touches.first else { state = .failed return } // 过滤按钮等UIControl的触摸 if let _ = touch.view as? UIControl { state = .failed return } if state == .possible { state = .recognized } } override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) { state = .failed } override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) { state = .failed } }
现在测试一下:
- 触摸视图空白区域:视图会正常触发
handleTap,背景色变化; - 触摸按钮:按钮能完整响应
Touch Up Inside事件,打印testBtnPressed!,同时视图不会有任何响应。
内容的提问来源于stack exchange,提问作者skyIsCoding
相关产品推荐
相关产品推荐

