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

UIView无法响应UITapGestureRecognizer,如何让选择器生效?

问题分析与解决方案

核心问题

点击无响应的根本原因是CustomView实例本身没有设置有效frame:你给optionView(CustomView的子视图)设置了位置和尺寸,但CustomView自身的frame始终是初始的CGRect(x:0,y:0,width:0,height:0),导致手势识别器的点击区域不存在,自然无法触发回调。

另外,每次viewWillAppear都会重复调用setBoiledType,导致optionView被多次添加到CustomView中,造成视图层级混乱,也会间接影响交互。


分步修复方案

1. 给CustomView本身设置有效frame

在viewWillAppear中,先设置CustomView实例的frame,再处理子视图的布局:

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    
    let yPos = boiledTypeLbl.frame.maxY + 40
    let itemWidth: CGFloat = 91
    let itemHeight: CGFloat = 128
    
    // 第一个视图
    firstTypeView.frame = CGRect(x: 38, y: yPos, width: itemWidth, height: itemHeight)
    firstTypeView.setBoiledType()
    firstTypeView.imageView.image = UIImage(named: "Soft egg png")
    firstTypeView.setLbl(x: firstTypeView.frame.width/2 + 20, y: firstTypeView.imageView.frame.maxY - 15, width: 50, height: 50, text: "Soft boiled")
    if firstTypeView.superview == nil {
        view.addSubview(firstTypeView)
    }
    
    // 第二个视图
    let secondX = firstTypeView.frame.maxX + 25
    secondTypeView.frame = CGRect(x: secondX, y: yPos, width: itemWidth, height: itemHeight)
    secondTypeView.setBoiledType()
    secondTypeView.imageView.image = UIImage(named: "Medium boiled")
    secondTypeView.setLbl(x: firstTypeView.frame.width/2 + 20, y: firstTypeView.imageView.frame.maxY - 15, width: 60, height: 50, text: "Medium boiled")
    if secondTypeView.superview == nil {
        view.addSubview(secondTypeView)
    }
    
    // 第三个视图
    let thirdX = secondTypeView.frame.maxX + 25
    thirdTypeView.frame = CGRect(x: thirdX, y: yPos, width: itemWidth, height: itemHeight)
    thirdTypeView.setBoiledType()
    thirdTypeView.imageView.image = UIImage(named: "Hard boiled")
    thirdTypeView.setLbl(x: firstTypeView.frame.width/2 + 20, y: firstTypeView.imageView.frame.maxY - 15, width: 50, height: 50, text: "Hard boiled")
    if thirdTypeView.superview == nil {
        view.addSubview(thirdTypeView)
    }
}

2. 修复CustomView的子视图布局逻辑

修改setBoiledType,避免重复添加子视图,同时让optionView匹配CustomView的大小:

func setBoiledType() {
    // 已添加过子视图则直接返回,避免重复添加
    guard optionView.superview == nil else { return }
    
    addSubview(optionView)
    // 用AutoLayout让optionView填满整个CustomView
    optionView.translatesAutoresizingMaskIntoConstraints = false
    NSLayoutConstraint.activate([
        optionView.topAnchor.constraint(equalTo: topAnchor),
        optionView.leadingAnchor.constraint(equalTo: leadingAnchor),
        optionView.trailingAnchor.constraint(equalTo: trailingAnchor),
        optionView.bottomAnchor.constraint(equalTo: bottomAnchor)
    ])
    
    // 保留原有的样式设置
    optionView.layer.masksToBounds = false
    optionView.layer.borderWidth = 1.0
    optionView.layer.borderColor = UIColor(named: "inActive")?.cgColor
    optionView.layer.cornerRadius = 10
    optionView.layer.shadowColor = UIColor.black.cgColor
    optionView.layer.shadowOffset = CGSizeMake(0, 5)
    optionView.backgroundColor = .white
    optionView.layer.shadowOpacity = 0.1
    optionView.layer.shadowRadius = 5

    imageView.frame = CGRect(x: -43, y: -40, width: 180, height: 126)
    optionView.addSubview(imageView)
    optionView.addSubview(optionTitle)
}

// 修改setFrame方法,设置CustomView自身的frame
func setFrame(x: CGFloat, y: CGFloat, width: CGFloat, height: CGFloat) {
    self.frame = CGRect(x: x, y: y, width: width, height: height)
}

3. 优化手势添加逻辑(可选)

可以用循环简化手势添加代码,避免重复创建相似的手势实例:

func addTapGestureToViews() {
    let tapAction = #selector(chooseType)
    [firstTypeView, secondTypeView, thirdTypeView].forEach { view in
        view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: tapAction))
    }
}

效果验证

修改完成后,CustomView实例拥有了有效点击区域,点击时会触发chooseType方法,进而调用optionTapped和viewOriginalState实现样式切换。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 05:25:02