ios 将参数传递给 Swift 中的 Selector
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/33443570/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
passing parameters to a Selector in Swift
提问by Cyrus Price
I am building an app for keeping track of reading assignments for a university course. Each ReadingAssignment has included a Bool value that indicates if the reader has finished reading the assignment. The ReadingAssignments are collected into WeeklyAssignment arrays. I want to have the user be able to touch a label and have a checkmark appear and show the assignment as completed. I would like this touch to also update the .checked property to true so I can persist the data. So, I am trying to have the gestureRecognizer call the labelTicked() method. This works and prints to the console. However, when I try to pass in the assignment parameter, it compiles, but crashes on the touch with an "unrecognized selector" error. I have read every topic i can find here, and haven't found the solution. They all say ":" signifies a Selector with parameters, but still no go. Can you see what I am doing wrong?
我正在构建一个应用程序,用于跟踪大学课程的阅读作业。每个 ReadingAssignment 都包含一个 Bool 值,指示读者是否已完成阅读作业。ReadingAssignments 被收集到 WeeklyAssignment 数组中。我希望用户能够触摸标签并显示复选标记并显示作业已完成。我希望此触摸也将 .checked 属性更新为 true,以便我可以保留数据。所以,我试图让gestureRecognizer 调用labelTicked() 方法。这有效并打印到控制台。但是,当我尝试传入赋值参数时,它会编译,但在触摸时崩溃并出现“无法识别的选择器”错误。我已经阅读了我可以在这里找到的每个主题,但还没有找到解决方案。他们都说“:” 表示带有参数的 Selector,但仍然没有通过。你能看出我做错了什么吗?
func configureCheckmark(cell: UITableViewCell, withWeeklyAssignment assignment: WeeklyAssignment) {
let checkLabel = cell.viewWithTag(1002) as! UILabel
checkLabel.userInteractionEnabled = true
let gestureRecognizer = UITapGestureRecognizer(target: self, action: Selector("labelTicked:assignment"))
checkLabel.addGestureRecognizer(gestureRecognizer)
}
@objc func labelTicked(assignment: WeeklyAssignment) {
assignment.toggleCheckmark()
if assignment.checked {
label.text = "??"
} else {
label.text = ""
}
}
I would also love to pass in the UILabel checkLabel so I can update it in the labelTicked() method. Thanks for your help.
我也很想传入 UILabel checkLabel 以便我可以在 labelTicked() 方法中更新它。谢谢你的帮助。
回答by Gamma
There are two distinct problems here:
这里有两个不同的问题:
The syntax for the selector is wrong; the
:
doesn't mark the beginning of a parameters part, it merely marks that this function takes parameters at all. So the tap recognizer should be initialized asUITapGestureRecognizer(target: self, action: "labelTicked:")
. Easy fix. That is the good news.The bad news: even with perfect syntax it will not work the way you set it up here. Your tap recognizer cannot pass a
WeeklyAssignment
object as a parameter. In fact, it cannot pass any custom parameter at all. At least, not like that.
选择器的语法错误;在
:
没有标记的参数部分的开始,它只是标记,这不起作用,需要的参数。因此,tap 识别器应初始化为UITapGestureRecognizer(target: self, action: "labelTicked:")
. 轻松修复。这是好消息。坏消息:即使有完美的语法,它也不会像你在这里设置的那样工作。您的点击识别器不能将
WeeklyAssignment
对象作为参数传递。事实上,它根本无法传递任何自定义参数。至少,不是那样。
What it can pass, however, is its sender
(which is usually the view the gesture recognizer is attached to). You can grab it by changing your method to
然而,它可以传递的是它的sender
(通常是手势识别器附加到的视图)。你可以通过改变你的方法来抓住它
func labelTicked(sender: AnyObject) {
(note that AnyObject
may be declared as a more specific type if you know exactly what to expect.)
(请注意,AnyObject
如果您确切地知道会发生什么,则可以将其声明为更具体的类型。)
Going through the sender, you could now theoretically infer which label it is that has been tapped, which data entity that labels corresponds to, and which state the checked
property of that entity is in. I think this would become very convoluted very quickly.
通过发送方,您现在可以从理论上推断出它是哪个标签被窃听,标签对应于哪个数据实体,以及该checked
实体的属性处于哪个状态。我认为这会很快变得非常复杂。
Seemingly straightforward things becoming convoluted is usually a good sign that we should take a step back and look for a better solution.
看似简单的事情变得复杂通常是一个好兆头,我们应该退后一步,寻找更好的解决方案。
I'd suggest dropping the whole GestureRecognizer
approach at this point, and instead exploit the fact that each cell in a table view already comes with its own "tap recongizing" functionality out of the box: the didSelectRowAtIndexPath:
method of the table view's delegate. There, you can easily use the supplied NSIndexPath
to retrieve the corresponding model entity from the data source to read and modify its parameters as you see fit. Via cellForRowAtIndexPath:
you can then get a reference to the correct cell and change its contents accordingly.
我建议GestureRecognizer
在这一点上放弃整个方法,而是利用表视图中的每个单元格已经带有开箱即用的“点击识别”功能的事实:didSelectRowAtIndexPath:
表视图的委托方法。在那里,您可以轻松地使用所提供的NSIndexPath
从数据源中检索相应的模型实体,以根据需要读取和修改其参数。通过cellForRowAtIndexPath:
那么你就可以得到正确的单元格的引用,并相应地改变它的内容。
Update for Swift 3:
Swift 3 更新:
As the Swift language evolves and using String-based selectors (e.g. "labelTicked:"
) is now flagged as deprecated, I think it's appropriate to provide a small update to the answer.
随着 Swift 语言的发展和使用基于字符串的选择器(例如"labelTicked:"
)现在被标记为已弃用,我认为对答案进行小幅更新是合适的。
Using more modern syntax, you could declare your function like this:
使用更现代的语法,您可以像这样声明您的函数:
@objc func labelTicked(withSender sender: AnyObject) {
and initialize your gesture recognizer like this, using #selector
:
并像这样初始化你的手势识别器,使用#selector
:
UITapGestureRecognizer(target: self, action: #selector(labelTicked(withSender:)))
回答by rob mayoff
The correct selector for that function is labelTicked:
. Furthermore, you can use a string directly as a selector. Thus:
该函数的正确选择器是labelTicked:
。此外,您可以直接使用字符串作为选择器。因此:
let gestureRecognizer = UITapGestureRecognizer(target: self, action: "labelTicked:")
But you can't arrange to pass an arbitrary object along to the method when the recognizer fires. A better way to do this is to create a subclass of UITableViewCell
. Let's call it AssignmentCell
. Give the subclass an assignment
property, and move the labelTicked:
method to AssignmentCell
.
但是您不能安排在识别器触发时将任意对象传递给该方法。更好的方法是创建UITableViewCell
. 让我们称之为AssignmentCell
。给子类一个assignment
属性,并将labelTicked:
方法移动到AssignmentCell
。
If you've designed the cell in your storyboard, you can add a tap recognizer to the label right in the storyboard, and wire the recognizer to the cell's labelTicked:
method in the storyboard.
如果您已经在故事板中设计了单元格,您可以在故事板中的标签上添加一个点击识别器,并将识别器连接到labelTicked:
故事板中单元格的方法。
In your table view data source's tableView(_:cellForRowAtIndexPath:)
, after you've dequeued the cell, set its assignment
property to the assignment for that row.
在表视图数据源的 中tableView(_:cellForRowAtIndexPath:)
,在单元格出列后,将其assignment
属性设置为该行的分配。