UITapGestureRecognizer在自定义View类中失效问题排查
排查自定义UIView中UITapGestureRecognizer不触发的问题
我帮你找到了问题所在,咱们一步步拆解分析:
问题根源
对比代码1和代码2的核心差异:
- 代码1中,红色UIView的
frame是相对于ViewController的主View设置的,整个视图完全处于父View的可见/可交互范围内,手势自然能正常触发。 - 代码2里,自定义MyView自身的尺寸是100x100,但它内部创建的红色子View的
frame是(x:100, y:100, width:100, height:100)——这意味着这个红色子View的所有区域都超出了MyView的边界(MyView的bounds范围是(0,0,100,100))。
UIKit的手势识别器默认只会响应父视图bounds范围内的触摸事件,即使子视图因为clipsToBounds=false能显示在父视图外面,超出部分也无法接收触摸交互。
解决方案
针对这个问题,有两种常用的解决思路:
方案1:调整子View的frame到父View边界内
直接修改红色子View的frame,让它完全处于MyView的bounds范围内:
func initView(){ // 将子View的原点改为(0,0),确保在MyView内部 let myView : UIView = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100)) myView.backgroundColor = .red myView.addGestureRecognizer(UITapGestureRecognizer(target:self,action:#selector(self.doSomethingOnTap))) addSubview(myView) }
方案2:允许父View响应子View超出边界的触摸
如果业务需求确实需要子View超出父View边界也能触发手势,可以重写MyView的point(inside:with:)方法,让系统判断触摸点是否在自身或子View的范围内:
class MyView : UIView { // ... 其他原有代码 ... override func point(inside point: CGPoint, with event: UIEvent?) -> Bool { // 先检查自身bounds内的触摸 if super.point(inside: point, with: event) { return true } // 遍历子View,检查触摸点是否在子View范围内 for subview in subviews { let convertedPoint = subview.convert(point, from: self) if subview.point(inside: convertedPoint, with: event) { return true } } return false } }
验证修改后的完整代码
以方案1为例,修改后的代码2可以正常触发手势:
import UIKit class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() let myView = MyView(frame: CGRect(x: 100, y: 100, width: 100, height: 100)) self.view.addSubview(myView) } } class MyView : UIView { override init(frame: CGRect) { super.init(frame: frame) initView() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } func initView(){ let myView : UIView = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100)) myView.backgroundColor = .red myView.addGestureRecognizer(UITapGestureRecognizer(target:self,action:#selector(self.doSomethingOnTap))) addSubview(myView) } @objc func doSomethingOnTap() { print("tapped") } }
内容的提问来源于stack exchange,提问作者S.Basnagoda
相关产品推荐
相关产品推荐

