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

Swift 4中touchesBegan监听未被调用的原因排查

解决你的UIControl子类touchesBegan不触发问题

Hey there, let's break down why your touchesBegan method isn't getting called when you tap your custom button:

核心原因

你的Btn类是UIControl的子类,但你在它内部嵌套了一个UIButton——而UIButton本身也是UIControl的子类,它会优先拦截触摸事件。当你点击按钮区域时,触摸事件会被内部的UIButton直接捕获并处理,根本不会传递到外层的Btn类,所以你重写的touchesBegan自然不会执行。

另外还有个小问题:你创建Btn实例时没有设置它的frame,默认是CGRect.zero,这会导致父类控件的可交互区域为0,即使内部按钮有尺寸,也会影响触摸事件的响应逻辑。

解决方案

根据你的需求,有两种处理方式:

方式一:移除内部的UIButton,直接自定义UIControl外观

如果你的需求只是创建一个自定义样式的可交互控件,完全不需要嵌套UIButton,直接给Btn类设置外观即可:

import UIKit
class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        // 给Btn设置明确的frame(或者用AutoLayout约束)
        let button = Btn(frame: CGRect(x: 100, y: 100, width: 50, height: 50))
        view.addSubview(button)
    }
}
class Btn: UIControl {
    override init(frame: CGRect) {
        super.init(frame: frame)
        backgroundColor = .green // 直接设置自身背景色
    }
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        backgroundColor = .green
    }
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        super.touchesBegan(touches, with: event)
        print("test") // 现在点击会正常打印了
    }
}

方式二:保留内部UIButton,调整事件响应逻辑

如果你确实需要保留内部的UIButton(比如复用它的某些内置功能),可以通过以下两种方式让外层Btn类接收到触摸事件:

方法1:禁用内部按钮的用户交互

让内部按钮不拦截触摸事件,把事件交给父类处理:

class Btn: UIControl {
    fileprivate let button: UIButton = {
        let swapButton = UIButton()
        let size = CGFloat(50)
        swapButton.frame.size = CGSize(width: size, height: size)
        swapButton.backgroundColor = UIColor.green
        swapButton.isUserInteractionEnabled = false // 禁用内部按钮的交互能力
        return swapButton
    }()
    // 其余代码不变...
}
方法2:转发内部按钮的事件到父类

如果需要内部按钮保留部分交互功能,可以给它添加事件监听,然后在回调里执行父类的逻辑:

class Btn: UIControl {
    fileprivate let button: UIButton = {
        let swapButton = UIButton(type: .custom)
        let size = CGFloat(50)
        swapButton.frame.size = CGSize(width: size, height: size)
        swapButton.backgroundColor = UIColor.green
        return swapButton
    }()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        addSubview(button)
        // 给内部按钮添加点击事件监听
        button.addTarget(self, action: #selector(internalButtonTapped), for: .touchUpInside)
    }
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        addSubview(button)
        button.addTarget(self, action: #selector(internalButtonTapped), for: .touchUpInside)
    }
    
    @objc private func internalButtonTapped() {
        print("Internal button tapped")
        // 这里可以手动触发父类的触摸逻辑,或者直接执行你需要的代码
        print("test") // 直接调用你的业务逻辑即可
    }
    
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        super.touchesBegan(touches, with: event)
        print("test")
    }
}

别忘了,创建Btn实例时一定要给它设置有效的frame或者添加AutoLayout约束,否则控件的可交互区域为0,也会导致触摸事件无法被捕获哦!

内容的提问来源于stack exchange,提问作者slava_slava

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 04:44:43