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
相关产品推荐
相关产品推荐

