如何在循环中创建的视图上处理 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 视图或集合视图可能更好,因为它们是由模型驱动的。