如何在循环中创建的视图上处理 UITapGestureRecognizer?
How to handle UITapGestureRecognizer on views created in loops?
我有一个垂直的 UIStackView,我在这个视图中附加了一个水平的 UIStackView 和一些 UILabel。我附加了 UITapGestureRecognizer 来处理点击操作。当我点击一行时如何更改标签(它是一个子视图)?
到目前为止,这是我的代码:
for article in articleType {
//Create a row
let row = UIStackView()
row.axis = NSLayoutConstraint.Axis.horizontal
//Create a label and indicator
let label = UILabel()
label.text = article.label
let indicator = UILabel()
indicator.text = "off"
//Add label to row and row to parent stack view
row.addSubView(label)
row.addSubView(indicator)
mainUiStackView.addArrangedSubview(row)
//Create tap handled
let tap = UITapGestureRecognizer(target: self, action: #selector(self.handleTap(sender:)))
row.addGestureRecognizer(tap)
}
@objc func handleTap(sender: UITapGestureRecognizer? = nil) {
print(sender.view)
//This is where i'm stuck, how can i change the second label's text to "on" for example?
sender.view.indicator.text = "on"
}
首先,确保您要点击的标签的 isUserInteractionEnabled
属性 设置为 true
,因为它默认设置为 false
。
@objc func handleTap(_ sender: UITapGestureRecognizer) {
guard let tappedView = sender.view else { return }
let location = sender.location(in: tappedView.superview)
if case let hitView as UILabel = tappedView.hitTest(location, with: nil) {
hitView.text = hitView.text == "off" ? "on" : "off"
}
}
您的代码执行一个循环并在局部变量 row
中创建一系列堆栈视图。然后,您不会将这些堆栈视图添加到您的视图层次结构中,因此它们会“掉在地上”。
对于这一行:
let row = UIStackView()
每次通过 for 循环后,您创建的堆栈视图将超出范围并被释放。
编辑:
在您指出之后,我现在看到您将 row
堆栈视图添加到 mainUiStackView
的位置。
我根据您所做的评论收集到您想要点击您的“行”堆栈视图之一,将该堆栈视图中的 indicator
标签设置为“打开”。
我建议构建一个行堆栈视图数组及其标签视图(作为结构)。然后找到被点击的行并将其标签设置为打开状态:
struct RowStackViewStruct {
let stackView: UIStackView
let label: UILabel
}
var rowStackViews = [RowStackViewStruct]()
for article in articleType {
//Create a row
let row = UIStackView()
row.axis = NSLayoutConstraint.Axis.horizontal
//Create a label and indicator
let label = UILabel()
label.text = article.label
let indicator = UILabel()
indicator.text = "off"
//Add label to row and row to parent stack view
row.addSubView(label)
row.addSubView(indicator)
mainUiStackView.addArrangedSubview(row)
//Create tap handled
let tap = UITapGestureRecognizer(target: self, action: #selector(self.handleTap(sender:)))
row.addGestureRecognizer(tap)
//Build a RowStackViewStruct for this row
let newRowStackViewStruct = RowStackViewStruct(stackView: row,
label: indicator)
rowStackViews.append(newRowStackViewStruct)
}
And then in your tap handler code:
@objc func handleTap(sender: UITapGestureRecognizer? = nil) {
print(sender.view)
if let aStruct = rowStackViews.first(where: { sender?.view == [=11=].stackView }) {
aStruct.label.text = "on"
}
我在 SO 编辑器中敲出了上面的代码。它可能包含轻微的语法错误,旨在作为指南,而不是您可以放入其中的复制粘贴代码。
看来您真的应该跟踪行的开关状态。看起来你应该有一个模型对象数组来表示你的行的状态,并使用它来构建和维护你的主要垂直堆栈视图。事实上,切换到 table 视图或集合视图可能更好,因为它们是由模型驱动的。
我有一个垂直的 UIStackView,我在这个视图中附加了一个水平的 UIStackView 和一些 UILabel。我附加了 UITapGestureRecognizer 来处理点击操作。当我点击一行时如何更改标签(它是一个子视图)?
到目前为止,这是我的代码:
for article in articleType {
//Create a row
let row = UIStackView()
row.axis = NSLayoutConstraint.Axis.horizontal
//Create a label and indicator
let label = UILabel()
label.text = article.label
let indicator = UILabel()
indicator.text = "off"
//Add label to row and row to parent stack view
row.addSubView(label)
row.addSubView(indicator)
mainUiStackView.addArrangedSubview(row)
//Create tap handled
let tap = UITapGestureRecognizer(target: self, action: #selector(self.handleTap(sender:)))
row.addGestureRecognizer(tap)
}
@objc func handleTap(sender: UITapGestureRecognizer? = nil) {
print(sender.view)
//This is where i'm stuck, how can i change the second label's text to "on" for example?
sender.view.indicator.text = "on"
}
首先,确保您要点击的标签的 isUserInteractionEnabled
属性 设置为 true
,因为它默认设置为 false
。
@objc func handleTap(_ sender: UITapGestureRecognizer) {
guard let tappedView = sender.view else { return }
let location = sender.location(in: tappedView.superview)
if case let hitView as UILabel = tappedView.hitTest(location, with: nil) {
hitView.text = hitView.text == "off" ? "on" : "off"
}
}
您的代码执行一个循环并在局部变量 row
中创建一系列堆栈视图。然后,您不会将这些堆栈视图添加到您的视图层次结构中,因此它们会“掉在地上”。
对于这一行:
let row = UIStackView()
每次通过 for 循环后,您创建的堆栈视图将超出范围并被释放。
编辑:
在您指出之后,我现在看到您将 row
堆栈视图添加到 mainUiStackView
的位置。
我根据您所做的评论收集到您想要点击您的“行”堆栈视图之一,将该堆栈视图中的 indicator
标签设置为“打开”。
我建议构建一个行堆栈视图数组及其标签视图(作为结构)。然后找到被点击的行并将其标签设置为打开状态:
struct RowStackViewStruct {
let stackView: UIStackView
let label: UILabel
}
var rowStackViews = [RowStackViewStruct]()
for article in articleType {
//Create a row
let row = UIStackView()
row.axis = NSLayoutConstraint.Axis.horizontal
//Create a label and indicator
let label = UILabel()
label.text = article.label
let indicator = UILabel()
indicator.text = "off"
//Add label to row and row to parent stack view
row.addSubView(label)
row.addSubView(indicator)
mainUiStackView.addArrangedSubview(row)
//Create tap handled
let tap = UITapGestureRecognizer(target: self, action: #selector(self.handleTap(sender:)))
row.addGestureRecognizer(tap)
//Build a RowStackViewStruct for this row
let newRowStackViewStruct = RowStackViewStruct(stackView: row,
label: indicator)
rowStackViews.append(newRowStackViewStruct)
}
And then in your tap handler code:
@objc func handleTap(sender: UITapGestureRecognizer? = nil) {
print(sender.view)
if let aStruct = rowStackViews.first(where: { sender?.view == [=11=].stackView }) {
aStruct.label.text = "on"
}
我在 SO 编辑器中敲出了上面的代码。它可能包含轻微的语法错误,旨在作为指南,而不是您可以放入其中的复制粘贴代码。
看来您真的应该跟踪行的开关状态。看起来你应该有一个模型对象数组来表示你的行的状态,并使用它来构建和维护你的主要垂直堆栈视图。事实上,切换到 table 视图或集合视图可能更好,因为它们是由模型驱动的。