如何在循环中创建的视图上处理 UITapGestureRecognizer?
Posted
技术标签:
【中文标题】如何在循环中创建的视图上处理 UITapGestureRecognizer?【英文标题】:How to handle UITapGestureRecognizer on views created in loops? 【发布时间】:2020-12-22 23:05:13 【问题描述】:我有一个垂直的 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"
【问题讨论】:
您不能点击堆栈视图。 @matt,为什么不呢?我刚刚创建了一个测试应用程序,在其中我将轻击手势识别器添加到排列的子视图和堆栈视图中,它们都按预期触发。 (我必须点击子视图之间的空间才能触发堆栈视图的点击手势识别器,但它起作用了。) @DuncanC 好吧,也许他们改变了这一点。过去,堆栈视图是一种特殊的视图。 所以每个堆栈视图将始终包含恰好 2 个标签,当用户点击给定堆栈视图时,您总是想进入堆栈视图并将其标签文本设置为“on”? 是的。我实际上想将复选框图像更改为开/关,但在示例代码中使用了 UILabel 所以它并不复杂,但逻辑将以相同的方式应用。 【参考方案1】:您的代码会循环并在局部变量row
中创建一系列堆栈视图。然后,您不会将这些堆栈视图添加到您的视图层次结构中,因此它们会“掉在地上”。
对于这一行:
let row = UIStackView()
每次通过您的 for 循环后,您创建的堆栈视图将超出范围并被释放。
编辑:
在您指出之后,我现在看到您将row
堆栈视图添加到mainUiStackView
的位置。
我根据您创建的 cmets 收集到,您希望在您的“行”堆栈视图之一上进行点击,将该堆栈视图中的 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 编辑器中敲出了上面的代码。它可能包含轻微的语法错误,仅供参考,而不是您可以放入的复制粘贴代码。
您似乎真的应该跟踪行的开关状态。看起来你应该有一个模型对象数组来代表你的行的状态,并使用它来构建和维护你的主垂直堆栈视图。事实上,切换到表格视图或集合视图可能会更好,因为它们是由模型驱动的。
【讨论】:
嗨!这部分代码实际上工作正常。我遇到的问题是如何在点击整行后访问特定的 UILabel 子视图。所以 sender.view.indicator.text 显然不起作用,但我不知道如何在没有 IBOutlet 的情况下访问这个开/关标签(我没有,因为我正在创建这些标签在代码循环中) 您发布的代码不完整,您对您想要做什么的描述也是如此。由于我在回答中指出的原因,您发布的代码不起作用。如果您试图点击堆栈视图,找出堆栈视图中的哪个视图被点击,请编辑您的问题以说明这一点,并清楚地说出来。必须回答 20 个问题才能弄清楚别人在问什么,这真是令人抓狂。 对不起,我不确定你的意思。我将整行添加到这一行中的另一个 UIStackView:mainUiStackView.addArrangedSubview(row)。点击操作在行上运行良好, print(sender.view) 正确地分别记录每行的操作。我不想在行内的 UILabels 中添加点击手势,只是在主行中。当点击主行时,我想更改该行内的标签文本之一。这是我在问题中描述的,我不知道如何使它更清楚。 我错过了那条线。请参阅我的答案的编辑。 谢谢,完美运行,只需更改一件事,更新您的答案。【参考方案2】:首先,确保您要点击的任何标签的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"
【讨论】:
对不起,我想我的问题并不清楚。所以我希望点击动作在整行中运行。当我点击行(无论如何,而不是特定的子视图)时,我想将子视图的 UILabel 值从关闭更改为打开。因此,如果我有第二个 UILabel 的 IBOutlet,这将很容易,但是由于我从代码和循环中生成它们,因此不确定如何在 handleTap 函数中访问这些标签。以上是关于如何在循环中创建的视图上处理 UITapGestureRecognizer?的主要内容,如果未能解决你的问题,请参考以下文章