如何在循环中创建的视图上处理 UITapGestureRecognizer?

问题描述

我有一个垂直的 UIStackView,我在这个视图中附加了一个水平的 UIStackView 和一些 UILabels。我附加了 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 == $0.stackView }) {
       aStruct.label.text = "on"
}

我在 SO 编辑器中敲出了上面的代码。它可能包含轻微的语法错误,旨在作为指南,而不是您可以插入的复制粘贴代码。

看来您真的应该跟踪行的开关状态。似乎您应该拥有一组代表行状态的模型对象,并使用它来构建和维护主垂直堆栈视图。事实上,切换到表格视图或集合视图可能会更好,因为它们是由模型驱动的。