parameter method examples enum closure apple ios swift closures

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)!!!") })