假设我有一个函数接受带有发件人的回调,如下所示:
func performAction(aNumber: Double, completion: (sender: UIButton) -> Void) {
// Does some stuff here
let button = getAButtonFromSomewhere()
completion(button)
}
因此,调用此函数的一种可能方法是通过传递现有函数来进行回调,而不是就地定义闭包:
performAction(10, completion: myCallback)
func myCallback(sender: UIButton) {
sender.setTitle("foo", forState: .Normal)
}
回到我对performAction
的定义中,如何定义完成块以接受UIButton
或其中的任何子类?
例如,假设我有一个名为UIButton
的{{1}}子类。所以在我的回调中,我只对接受CustomButton
感兴趣。我想这样做:
CustomButton
但是编译器不会允许它,因为performAction(10, completion: myCallback)
// This produces a compiler error:
func myCallback(sender: CustomButton) {
sender.setTitle("foo", forState: .Normal)
}
// This works, but forces me to cast to my custom class:
func myCallback(sender: UIButton) {
let realButton = sender as! CustomButton
realButton.setTitle("foo", forState: .Normal)
}
的定义要求回调专门接受performAction
(即使UIButton
是 a CustomButton
子类)。
我希望UIButton
是通用的,以便它可以打包在一个库中,并与任何performAction
子类一起使用。这可以在Swift中做到吗?
编辑:我试图简化上面的例子我正在做的事情,但我认为这只是引起了混乱。这是我正在努力工作的实际代码,感谢@ luk2302的一些改进:
UIButton
现在唯一突破的部分是我评论的行,public extension UIButton {
private class Action: AnyObject {
private var function: Any
init(function: Any) {
self.function = function
}
}
// Trickery to add a stored property to UIButton...
private static var actionsAssocKey: UInt8 = 0
private var action: Action? {
get {
return objc_getAssociatedObject(self, &UIButton.actionsAssocKey) as? Action
}
set(newValue) {
objc_setAssociatedObject(self, &UIButton.actionsAssocKey, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN)
}
}
internal func performAction(sender: UIButton) {
if let function = self.action!.function as? () -> Void {
function()
// THIS IS WHERE THINGS BREAK NOW:
} else if let function = self.action!.function as? (sender: self.Type) -> Void {
function(sender: self)
}
}
public func addTarget(forControlEvents event: UIControlEvents, action: () -> Void) {
self.action = Action(function: action)
self.addTarget(self, action: "performAction:", forControlEvents: event)
}
public func addTarget<B: UIButton>(forControlEvents: UIControlEvents, actionWithSender: (sender: B) -> Void) {
self.action = Action(function: actionWithSender)
self.addTarget(self, action: "performAction:", forControlEvents: forControlEvents)
}
}
((sender: self.Type)
是self
或其中的某个子类。)
所以这稍微偏离了我原来的问题,但我怎样才能将UIButton
转换为接受与function
相同类型的发件人的闭包?如果我对类型进行硬编码,则此代码可以正常工作,但它应该能够用于任何 self
子类。
答案 0 :(得分:1)
您可以将UIButton子类设为performAction
函数的泛型参数,但是在将其传递给回调之前,需要先将按钮转换为除非你也有一种“获得”正确类型按钮的通用方法。
// performAction() works with any type of UIButton
func performAction<B: UIButton>(aNumber: Double, completion: (sender: B) -> Void)
{
// Assuming getAButtonFromSomewhere returns UIButton, and not B, you must cast it.
if let button = getAButtonFromSomewhere() as? B {
completion(sender: button)
}
}
答案 1 :(得分:1)
问问自己:如果将(CustomButton -> Void)
类型的闭包传递为completion
,然后getAButtonFromSomewhere
返回UIButton
的实例,会发生什么?然后代码无法调用闭包,因为UIButton
不是CustomButton
。
编译器根本不允许您将(CustomButton -> Void)
传递给(UIButton -> Void)
,因为(CustomButton -> Void)
比(UIButton -> Void)
更具限制性。请注意,您可以将(UIButton -> Void)
传递给(CustomButton -> Void)
类型的闭包,因为(UIButton -> Void)
较少限制 - 您可以将传递的所有内容传递给第一个到第二个好。
因此,使func
使用泛型类型,如@jtbandes建议或使用您的初始方法稍微改进一下:
func myCallback(sender: UIButton) {
if let realButton = sender as? CustomButton {
realButton.setTitle("foo", forState: .Normal)
}
}
只要setTitle
的返回值不是getAButtonFromSomewhere
,两种解决方案都会导致CustomButton
无法调用。