问题描述
我想将 UIButton 连接到一段代码——根据我的发现,在 Swift 中执行此操作的首选方法仍然是使用 addTarget(target: AnyObject?, action: Selector, forControlEvents: UIControlEvents)
函数.这使用 Selector
构造大概是为了向后兼容 Obj-C 库.我想我理解 Obj-C 中 @selector
的原因——能够引用方法,因为在 Obj-C 中方法不是一等值.
I want to hook up a UIButton to a piece of code – from what I have found, the preferred method to do this in Swift is still to use the addTarget(target: AnyObject?, action: Selector, forControlEvents: UIControlEvents)
function. This uses the Selector
construct presumably for backwards compatibility with Obj-C libraries. I think I understand the reason for @selector
in Obj-C – being able to refer to a method since in Obj-C methods are not first-class values.
不过,在 Swift 中,函数是一等值.有没有办法将 UIButton 连接到闭包,类似于:
In Swift though, functions are first-class values. Is there a way to connect a UIButton to a closure, something similar to this:
// -- Some code here that sets up an object X
let buttonForObjectX = UIButton()
// -- configure properties here of the button in regards to object
// -- for example title
buttonForObjectX.addAction(action: {() in
// this button is bound to object X, so do stuff relevant to X
}, forControlEvents: UIControlEvents.TouchUpOutside)
据我所知,上述情况目前是不可能的.考虑到 Swift 看起来它的目标是变得非常实用,这是为什么呢?这两个选项显然可以共存以实现向后兼容性.为什么这不像 JS 中的 onClick() 那样工作?似乎唯一将 UIButton 连接到目标-动作对的方法是使用仅出于向后兼容性原因而存在的东西(Selector
).
To my knowledge, the above is currently not possible. Considering that Swift looks like it's aiming to be a quite functional, why is this? The two options could clearly co-exist for backwards compatibility. Why doesn't this work more like onClick() in JS? It seems that the only way to hook up a UIButton to a target-action pair is to use something that exists solely for backwards compatibility reasons (Selector
).
我的用例是在循环中为不同的对象创建 UIButton,然后将每个对象连接到一个闭包.(设置标签/在字典中查找/子类化 UIButton 是肮脏的半解决方案,但我对如何在功能上做到这一点感兴趣,即这种关闭方法)
My use case is to create UIButtons in a loop for different objects, and then hook each up to a closure. (Setting a tag / looking up in a dictionary / subclassing UIButton are dirty semi-solutions, but I'm interested in how to do this functionally, ie this closure approach)
推荐答案
您可以通过添加辅助闭包包装器 (ClosureSleeve) 并将其作为关联对象添加到控件中来将 target-action 替换为闭包以使其保留.
You can replace target-action with a closure by adding a helper closure wrapper (ClosureSleeve) and adding it as an associated object to the control so it gets retained.
这与 n13 的答案中的解决方案类似.但我发现它更简单、更优雅.更直接地调用闭包并自动保留包装器(作为关联对象添加).
This is a similar solution to the one in n13's answer. But I find it simpler and more elegant. The closure is invoked more directly and the wrapper is automatically retained (added as an associated object).
class ClosureSleeve {
let closure: () -> ()
init(attachTo: AnyObject, closure: @escaping () -> ()) {
self.closure = closure
objc_setAssociatedObject(attachTo, "[(arc4random())]", self, .OBJC_ASSOCIATION_RETAIN)
}
@objc func invoke() {
closure()
}
}
extension UIControl {
func addAction(for controlEvents: UIControlEvents = .primaryActionTriggered, action: @escaping () -> ()) {
let sleeve = ClosureSleeve(attachTo: self, closure: action)
addTarget(sleeve, action: #selector(ClosureSleeve.invoke), for: controlEvents)
}
}
用法:
button.addAction {
print("Hello")
}
它会自动挂钩到 .primaryActionTriggered
事件,该事件等于 UIButton 的 .touchUpInside
.
It automatically hooks to the .primaryActionTriggered
event which equals to .touchUpInside
for UIButton.
这篇关于将 UIButton 连接到关闭?(迅速,目标行动)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!