You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义UIGestureRecognizer后按钮失效,如何实现视图与按钮事件共存?

嘿,我帮你搞定这个手势和按钮冲突的问题~

问题根源分析

你的自定义SingleTouchDownGestureRecognizer目前有两个核心问题:

  1. 默认cancelsTouchesInView = true,当手势被标记为.recognized后,系统会直接拦截后续的触摸事件(比如touchesEnded),导致按钮收不到完整的触摸流程,自然触发不了Touch Up Inside事件。
  2. 没有判断触摸区域是否属于子控件(按钮),导致按钮上的触摸也会触发视图的手势响应,不符合你“触摸按钮时视图无需响应”的需求。

解决方案&代码修改

我们从两个方向调整代码就能解决问题:

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 08:50:38