swift closures selector

¿Puedo hacer que#selector se refiera a un cierre en Swift?



uibarbuttonitem swift 4 (9)

Ahora es posible. He creado una esencia para los selectores basados ​​en bloques en Swift 4.

https://gist.github.com/cprovatas/98ff940140c8744c4d1f3bcce7ba4543

Uso:

UIButton().addTarget(Selector, action: Selector { debugPrint("my code here") }, for: .touchUpInside)

Quiero hacer que un argumento selector de mi método se refiera a una propiedad de cierre, ambas existen en el mismo ámbito. Por ejemplo,

func backgroundChange() { self.view.backgroundColor = UIColor.blackColor() self.view.alpha = 0.55 let backToOriginalBackground = { self.view.backgroundColor = UIColor.whiteColor() self.view.alpha = 1.0 } NSTimer.scheduledTimerWithTimeInterval(0.5, target: self, selector: #selector(backToOriginalBackground), userInfo: nil, repeats: false) }

Sin embargo, esto muestra un error: el Argument of #selector cannot refer to a property .

Por supuesto, puedo definir un método nuevo e independiente y moverle la implementación del cierre, pero quiero mantenerlo frugal para una implementación tan pequeña.

¿Es posible establecer un cierre en el argumento #selector ?


Así que mi respuesta a tener un selector asignado a un closure de manera rápida es similar a algunas de las respuestas ya, pero pensé que compartiría un ejemplo de la vida real de cómo lo hice dentro de una extensión UIViewController .

fileprivate class BarButtonItem: UIBarButtonItem { var actionCallback: ( () -> Void )? func buttonAction() { actionCallback?() } } fileprivate extension Selector { static let onBarButtonAction = #selector(BarButtonItem.buttonAction) } extension UIViewController { func createBarButtonItem(title: String, action: @escaping () -> Void ) -> UIBarButtonItem { let button = BarButtonItem(title: title, style: .plain, target nil, action: nil) button.actionCallback = action button.action = .onBarButtonAction return button } } // Example where button is inside a method of a UIViewController // and added to the navigationItem of the UINavigationController let button = createBarButtonItem(title: "Done"){ print("Do something when done") } navigationItem.setLeftbarButtonItems([button], animated: false)


Como observa @ gnasher729, esto no es posible porque los selectores son solo nombres de métodos, no métodos en sí mismos. En el caso general, usaría dispatch_after aquí, pero en este caso particular, la mejor herramienta de IMO es UIView.animateWithDuration , porque es exactamente para qué sirve esa función, y es muy fácil ajustar la transición:

UIView.animateWithDuration(0, delay: 0.5, options: [], animations: { self.view.backgroundColor = UIColor.whiteColor() self.view.alpha = 1.0 }, completion: nil)


Intenté esto para UIBarButtonItem al menos:

private var actionKey: Void? extension UIBarButtonItem { private var _action: () -> () { get { return objc_getAssociatedObject(self, &actionKey) as! () -> () } set { objc_setAssociatedObject(self, &actionKey, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } convenience init(title: String?, style: UIBarButtonItemStyle, action: @escaping () -> ()) { self.init(title: title, style: style, target: nil, action: #selector(pressed)) self.target = self self._action = action } @objc private func pressed(sender: UIBarButtonItem) { _action() } }

Entonces puedes hacer esto:

navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Test", style: .plain, action: { print("Hello World!") })


Mi solución fue crear una variable de bloque de clase como:

let completionBlock: () -> () = nil

Cree un método que llame a este compleciónBloque:

func completed(){ self.completionBlock!() }

Y dentro de donde quiero poner mi selector como un bloque que hice:

func myFunc(){ self.completionBlock = {//what I want to be done} NSTimer.scheduledTimerWithTimeInterval(0.5, target: self, selector: #selector(Myclass.completed), userInfo: nil, repeats: false) }


No directamente, pero algunas soluciones son posibles. Echa un vistazo al siguiente ejemplo.

/// Target-Action helper. final class Action: NSObject { private let _action: () -> () init(action: () -> ()) { _action = action super.init() } func action() { _action() } } let action1 = Action { print("action1 triggered") } let button = UIButton() button.addTarget(action1, action: #selector(action1.action), forControlEvents: .TouchUpInside)


No, #selector se refiere a un método Objective-C.

Sin embargo, puede hacer algo mucho mejor: agregue una extensión a NSTimer que le permita crear un temporizador programado no con un objetivo y un selector, sino con un cierre.


Puede usar ActionClosurable que es compatible con UIControl, UIButton, UIRefreshControl, UIGestureRecognizer y UIBarButtonItem. https://github.com/takasek/ActionClosurable

A continuación se muestra un ejemplo de UIBarButtonItem.

// UIBarButtonItem let barButtonItem = UIBarButtonItem(title: "title", style: .plain) { _ in print("barButtonItem title") }


Si cambia el alcance del bloque a un alcance de clase en lugar de a una función y mantiene una referencia para cerrar allí.

Podrías invocar ese cierre con una función. en la clase. Así que de esa manera puedes invocar ese cierre como un selector.

Algo como esto:

class Test: NSObject { let backToOriginalBackground = { } func backgroundChange() { NSTimer.scheduledTimerWithTimeInterval(0.5, target: self, selector: #selector(test), userInfo: nil, repeats: false) } func test() { self.backToOriginalBackground() } }