Предположим, у меня есть функция, которая принимает функцию обратного вызова с отправителем, как это:Swift: Accept замыкание с изменяющимися параметрами
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
под названием 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
специально (несмотря на то, CustomButton
являетсяUIButton
подкласс).
Я бы хотел, чтобы performAction
был общим, чтобы его можно было упаковать в библиотеку и работать с любым подклассом UIButton
. Можно ли это сделать в Swift?
EDIT: Я попытался упростить то, что я делаю, с приведенным выше примером, но я думаю, что это просто вызвало путаницу. Вот фактический код, который я пытаюсь сделать работу, с некоторыми улучшениями благодаря @ luk2302:
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
? Этот код работает отлично, если я жестко программирую тип, но он должен иметь возможность работать для любогоUIButton
подкласс.
Как работает мифический 'getAButtonFromSomewhere()' на самом деле? – jtbandes
@jtbandes Это был просто пример, поэтому мне не нужно публиковать всю свою реализацию здесь. Ради вопроса, мы можем с уверенностью предположить, что он всегда возвращает «CustomButton» или любой класс, который ожидает пользователь. – hundley