为什么我的UILabel在嵌套视图中没有收到触摸事件/如何测试响应链?

问题描述

我发现了很多关于不接收触摸事件的类似问题,并且我了解在某些情况下,可能需要编写自定义的hitTest函数-但我还阅读到响应者链将遍历层次结构中的视图和viewControllers -而且我不明白为什么我的实施需要自定义hitTest。

我正在寻找说明和/或指向文档的链接,以解释如何测试响应者链。 Xcode 10.2.1中出现此问题。

我的情况(我不使用情节提要):

  • 我有一个mainViewController,它提供了一个全屏视图以及一个ImageView和一些Labels。我已将TapGestureRecognizers附加到ImageView和标签之一-它们都可以正常工作。
  • 当我点击标签时,我添加一个子viewController,该视图作为mainViewController的子视图。该视图被限制为仅覆盖屏幕的右半部分。
  • 子viewController包含一个垂直的堆栈视图,该视图包含3个splittedSubviews。
  • 每个locatedSubview包含一个Label和一个水平StackView。
  • 每个水平stackView都包含一个带有标签的视图作为子视图。
  • 子视图中的Label将isUserInteractionEnabled标志设置为True,并添加TapGestureRecognizer。
  • 这些是子级ViewController中唯一设置了“ isUserInteractionEnabled”的对象。

Label的嵌套非常深,但是由于这是直接的父级/子级层次结构(与属于NavigationController的2个视图相对),我希望Label处于正常的响应者链中并正常运行。堆栈视图是否改变了这种行为?我是否需要在某些视图上将'isUserInteractionEnabled'值显式设置为False?有什么方法可以将日志记录添加到ResponderChain中,以便查看检查的视图并找出被阻止的位置?

阅读this StackOverflow post后,我尝试在viewDidLayoutSubviews()中添加手势识别器,而不是下面显示内容-但它们仍不接收点击事件。

在此先感谢您提供建议或帮助。

以下是标签代码,该代码未响应我的轻击事件及其应调用的轻击事件:

    func makeColorItem(colorName:String,bgColor:UIColor,fgColor:UIColor) -> UIView {
    let colorNumber:Int = colorLabelDict.count
    let colorView:UIView = {
        let v = UIView()
        v.tag = 700 + colorNumber
        v.backgroundColor = .clear
        v.contentMode = .center
        return v
    }()
    self.view.addSubview(colorView)
    let tapColorGR:UITapGestureRecognizer = UITapGestureRecognizer(target: self,action: #selector(tapColor))
    let colorChoice: UILabel = {
        let l = UILabel()
        l.tag = 700 + colorNumber
        l.isUserInteractionEnabled = true
        l.addGestureRecognizer(tapColorGR)
        l.text = colorName
        l.textAlignment = .center
        l.textColor = fgColor
        l.backgroundColor = bgColor
        l.font = UIFont.systemFont(ofSize: 24,weight: .bold)
        l.layer.borderColor = fgColor.cgColor
        l.layer.borderWidth = 1
        l.layer.cornerRadius = 20
        l.layer.masksToBounds = true
        l.adjustsFontSizetoFitWidth = true
        l.translatesAutoresizingMaskIntoConstraints = false
        l.widthAnchor.constraint(equalToConstant: 100)
        return l
    }()
    colorView.addSubview(colorChoice)
    colorChoice.centerXAnchor.constraint(equalTo: colorView.centerXAnchor).isActive = true
    colorChoice.centerYAnchor.constraint(equalTo: colorView.centerYAnchor).isActive = true
    colorChoice.heightAnchor.constraint(equalToConstant: 50).isActive = true
    colorChoice.widthAnchor.constraint(equalToConstant: 100).isActive = true
    colorLabelDict[colorNumber] = colorChoice
    return colorView
}

    @objc func tapColor(sender:UITapGestureRecognizer) {
    print("A Color was tapped...with tag:\(sender.view?.tag ?? -1)")
    if let cn = sender.view?.tag {
        colorNumber = cn
        let v = colorLabelDict[cn]
        if let l = (v?.subviews.first as? UILabel) {
            print("The \(l.text) label was tapped.")
        }
    }
}

解决方法

您似乎没有被水龙头识别的主要原因似乎是因为您要添加UILabel作为UIView的子视图,但您并未给出{{1} }任何约束。因此,视图的宽度和高度为零,并且标签位于视图的边界之外

没有看到所有代码,就好像您不需要带有标签的额外视图。

看看这个...它将在主视图中添加一个垂直的堆栈视图(以X和Y为中心),并在堆栈视图中添加“ colorChoice”标签:

UIView

运行结果:

enter image description here

这是3个class TestViewController: UIViewController { let stack: UIStackView = { let v = UIStackView() v.axis = .vertical v.spacing = 4 return v }() var colorLabelDict: [Int: UIView] = [:] override func viewDidLoad() { super.viewDidLoad() let v1 = makeColorLabel(colorName: "red",bgColor: .red,fgColor: .white) let v2 = makeColorLabel(colorName: "green",bgColor: .green,fgColor: .black) let v3 = makeColorLabel(colorName: "blue",bgColor: .blue,fgColor: .white) [v1,v2,v3].forEach { stack.addArrangedSubview($0) } stack.translatesAutoresizingMaskIntoConstraints = false view.addSubview(stack) NSLayoutConstraint.activate([ stack.centerXAnchor.constraint(equalTo: view.centerXAnchor),stack.centerYAnchor.constraint(equalTo: view.centerYAnchor),]) } func makeColorLabel(colorName:String,bgColor:UIColor,fgColor:UIColor) -> UILabel { let colorNumber:Int = colorLabelDict.count // create tap gesture recognizer let tapColorGR:UITapGestureRecognizer = UITapGestureRecognizer(target: self,action: #selector(tapColor)) let colorChoice: UILabel = { let l = UILabel() l.tag = 700 + colorNumber l.addGestureRecognizer(tapColorGR) l.text = colorName l.textAlignment = .center l.textColor = fgColor l.backgroundColor = bgColor l.font = UIFont.systemFont(ofSize: 24,weight: .bold) l.layer.borderColor = fgColor.cgColor l.layer.borderWidth = 1 l.layer.cornerRadius = 20 l.layer.masksToBounds = true l.adjustsFontSizeToFitWidth = true l.translatesAutoresizingMaskIntoConstraints = false // default .isUserInteractionEnabled for UILabel is false,so enable it l.isUserInteractionEnabled = true return l }() NSLayoutConstraint.activate([ // label height: 50,width: 100 colorChoice.heightAnchor.constraint(equalToConstant: 50),colorChoice.widthAnchor.constraint(equalToConstant: 100),]) // assign reference to this label in colorLabelDict dictionary colorLabelDict[colorNumber] = colorChoice // return newly created label return colorChoice } @objc func tapColor(sender:UITapGestureRecognizer) { print("A Color was tapped...with tag:\(sender.view?.tag ?? -1)") // unwrap the view that was tapped,make sure it's a UILabel guard let tappedView = sender.view as? UILabel else { return } let cn = tappedView.tag let colorNumber = cn print("The \(tappedView.text ?? "No text") label was tapped.") } } ,点击每个将触发UILabel函数,将其打印到调试控制台:

tapColor()