为什么我的嵌套视图中的 UILabel 没有接收到触摸事件/我如何测试响应链?
Why doesn't my UILabel in a nested view receive touch events / How can I test the Responder Chain?
我发现了很多关于不接收触摸事件的类似问题,我知道在某些情况下,可能需要编写自定义 hitTest 函数 - 但我还了解到响应链将遍历视图并且 viewControllers 在层次结构中 - 我不明白为什么我的实现需要自定义 hitTest。
我正在寻找解释 and/or 和 link 的文档,该文档解释了如何测试响应程序链。此问题发生在 Xcode 10.2.1.
我的场景(我没有使用 Storyboard):
- 我有一个 mainViewController,它提供了带有 ImageView 和一些标签的全屏视图。我已将 TapGestureRecognizers 附加到 ImageView 和其中一个标签 - 它们都可以正常工作。
- 当我点击标签时,我添加了一个子视图 viewController 并且它作为主视图的子视图 ViewController。视图被限制为仅覆盖屏幕的右半部分。
- 子 viewController 包含一个垂直堆栈视图,其中包含 3 个 arrangedSubview。
- 每个 arrangedSubview 包含一个 Label 和一个水平 StackView。
- 每个水平 stackView 都包含一个带有标签的视图作为子视图。
- 子视图中的 Label 将其 isUserInteractionEnabled 标志设置为 True 并添加了 TapGestureRecognizer。
- 这些是子 ViewController 中唯一设置了 'isUserInteractionEnabled' 的对象。
标签的嵌套相当深,但由于这是一个直接的 parent/child 层次结构(与属于 NavigationController 的 2 个视图相反),我希望标签位于正常的响应链中并正常运行。 Stack View 会改变这种行为吗?我是否需要在某些视图上将 'isUserInteractionEnabled' 值显式设置为 False?有什么方法可以将日志记录添加到 ResponderChain,以便我可以查看它检查了哪些视图并找出它被阻止的位置?
阅读后 我尝试在 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
的子视图,但您没有提供 UIView
任何约束。所以视图最终的宽度和高度为零,并且标签存在于视图的边界之外。
没有看到你的所有代码,看起来你不需要包含标签的额外视图。
看看这个...它将向主视图添加一个垂直堆栈视图 - 居中的 X 和 Y - 并将“colorChoice”标签添加到堆栈视图:
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([=10=])
}
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.")
}
}
运行 的结果:
那是 3 UILabel
s,点击每一个都会触发 tapColor()
功能,将其打印到调试控制台:
A Color was tapped...with tag:700
The red label was tapped.
A Color was tapped...with tag:701
The green label was tapped.
A Color was tapped...with tag:702
The blue label was tapped.
我发现了很多关于不接收触摸事件的类似问题,我知道在某些情况下,可能需要编写自定义 hitTest 函数 - 但我还了解到响应链将遍历视图并且 viewControllers 在层次结构中 - 我不明白为什么我的实现需要自定义 hitTest。
我正在寻找解释 and/or 和 link 的文档,该文档解释了如何测试响应程序链。此问题发生在 Xcode 10.2.1.
我的场景(我没有使用 Storyboard):
- 我有一个 mainViewController,它提供了带有 ImageView 和一些标签的全屏视图。我已将 TapGestureRecognizers 附加到 ImageView 和其中一个标签 - 它们都可以正常工作。
- 当我点击标签时,我添加了一个子视图 viewController 并且它作为主视图的子视图 ViewController。视图被限制为仅覆盖屏幕的右半部分。
- 子 viewController 包含一个垂直堆栈视图,其中包含 3 个 arrangedSubview。
- 每个 arrangedSubview 包含一个 Label 和一个水平 StackView。
- 每个水平 stackView 都包含一个带有标签的视图作为子视图。
- 子视图中的 Label 将其 isUserInteractionEnabled 标志设置为 True 并添加了 TapGestureRecognizer。
- 这些是子 ViewController 中唯一设置了 'isUserInteractionEnabled' 的对象。
标签的嵌套相当深,但由于这是一个直接的 parent/child 层次结构(与属于 NavigationController 的 2 个视图相反),我希望标签位于正常的响应链中并正常运行。 Stack View 会改变这种行为吗?我是否需要在某些视图上将 'isUserInteractionEnabled' 值显式设置为 False?有什么方法可以将日志记录添加到 ResponderChain,以便我可以查看它检查了哪些视图并找出它被阻止的位置?
阅读后
提前感谢任何可以提供建议或帮助的人。
这是不响应我的点击事件的标签的代码以及它应该调用的点击事件:
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
的子视图,但您没有提供 UIView
任何约束。所以视图最终的宽度和高度为零,并且标签存在于视图的边界之外。
没有看到你的所有代码,看起来你不需要包含标签的额外视图。
看看这个...它将向主视图添加一个垂直堆栈视图 - 居中的 X 和 Y - 并将“colorChoice”标签添加到堆栈视图:
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([=10=])
}
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.")
}
}
运行 的结果:
那是 3 UILabel
s,点击每一个都会触发 tapColor()
功能,将其打印到调试控制台:
A Color was tapped...with tag:700
The red label was tapped.
A Color was tapped...with tag:701
The green label was tapped.
A Color was tapped...with tag:702
The blue label was tapped.