ios - method - swift closure as function parameter
Uso rĂ¡pido del argumento selector como un cierre (4)
Me preguntaba si era posible pasar una función a una acción de botón (que normalmente es un selector).
Por ejemplo, normalmente diría:
UIBarButtonItem(title: "Press", style: .Done, target: self, action: "functionToCall")
func functionToCall() {
// Do something
}
Pero me preguntaba si es posible hacer algo como:
UIBarButtonItem(title: "Press", style: .Done, target: self, action: {
// Do Something
})
La razón por la que quiero hacer esto es porque mi función es super simple y parece que sería más ordenada y más rápida, con el énfasis que ponen en los cierres.
Aquí hay una solución actualizada para Swift 3.
class BlockBarButtonItem: UIBarButtonItem {
private var actionHandler: ((Void) -> Void)?
convenience init(title: String?, style: UIBarButtonItemStyle, actionHandler: ((Void) -> Void)?) {
self.init(title: title, style: style, target: nil, action: #selector(barButtonItemPressed))
self.target = self
self.actionHandler = actionHandler
}
convenience init(image: UIImage?, style: UIBarButtonItemStyle, actionHandler: ((Void) -> Void)?) {
self.init(image: image, style: style, target: nil, action: #selector(barButtonItemPressed))
self.target = self
self.actionHandler = actionHandler
}
func barButtonItemPressed(sender: UIBarButtonItem) {
actionHandler?()
}
}
Desafortunadamente no es posible con los inicializadores proporcionados por Apple. La forma en que esto funciona en segundo plano es con la reflexión, y proporcionar un cierre es algo completamente diferente que actualmente no se admite.
Es posible que pueda crear una solución personalizada con algo de piratería informática o Apple puede introducirla en el futuro.
Esta es una solución alternativa sin subclasificación:
extension UIBarButtonItem {
private struct AssociatedObject {
static var key = "action_closure_key"
}
var actionClosure: (()->Void)? {
get {
return objc_getAssociatedObject(self, &AssociatedObject.key) as? ()->Void
}
set {
objc_setAssociatedObject(self, &AssociatedObject.key, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
target = self
action = #selector(didTapButton(sender:))
}
}
@objc func didTapButton(sender: Any) {
actionClosure?()
}
}
Se basa en los objetos asociados del tiempo de ejecución de Objective-C para agregar una propiedad de cierre / bloque.
Cuando se establece, cambia el objetivo a sí mismo y apunta el selector a una nueva función que llama al cierre, si existe.
Con esto, en cualquier momento solo puede configurar el actionClosure
de cualquier UIBarButtonItem
y esperar que todo funcione, aquí hay un ejemplo:
let button = UIBarButtonItem(
barButtonSystemItem: .action,
target: nil, action: nil
)
button.actionClosure = {
print("hello")
}
Una publicación en Reddit explica una solución para esto con un componente personalizado: https://www.reddit.com/r/swift/comments/3fjzap/creating_button_action_programatically_using
Para usarlo, tuve que agregar el botón programáticamente en lugar de hacerlo a través del guión gráfico. Así fue como lo hice.
let tempVariableIWantToReference = "Classy"
navigationTitle.leftBarButtonItem = BlockBarButtonItem.init(
title: "< Back", style: UIBarButtonItemStyle.Plain,
actionHandler: { () -> Void in
print("Hey I''m in a closure")
print("I can even reference temporary variables. /(self.tempVariableIWantToReference)!!!")
})