Je veux faire un argument selector
de ma méthode faire référence à une propriété de fermeture, les deux existent dans la même portée. Par exemple,
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)
}
Cependant, cela indique une erreur: Argument of #selector cannot refer to a property
.
Bien sûr, je peux définir une nouvelle méthode distincte et lui transférer l’implémentation de la fermeture, mais je veux la garder sobre pour une implémentation aussi petite.
Est-il possible de définir une clôture sur #selector
argument?
Comme @ gnasher729 le remarque, cela n'est pas possible car les sélecteurs ne sont que des noms de méthodes, pas des méthodes elles-mêmes. Dans le cas général, j’utiliserais dispatch_after
ici, mais dans ce cas particulier, le meilleur outil IMO est UIView.animateWithDuration
, car c’est à quoi sert cette fonction et il est très facile d’ajuster la transition:
UIView.animateWithDuration(0, delay: 0.5, options: [], animations: {
self.view.backgroundColor = UIColor.whiteColor()
self.view.alpha = 1.0
}, completion: nil)
Pas directement, mais certaines solutions de contournement sont possibles. Regardez l'exemple suivant.
/// 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)
J'ai essayé ceci pour UIBarButtonItem au moins:
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()
}
}
Ensuite, vous pouvez faire ceci:
navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Test", style: .plain, action: {
print("Hello World!")
})
Vous pouvez utiliser ActionClosurable qui prend en charge UIControl, UIButton, UIRefreshControl, UIGestureRecognizer et UIBarButtonItem . https://github.com/takasek/ActionClosurable
Ci-dessous, exemple de UIBarButtonItem
// UIBarButtonItem
let barButtonItem = UIBarButtonItem(title: "title", style: .plain) { _ in
print("barButtonItem title")
}
Non, #selector fait référence à une méthode Objective-C.
Vous pouvez cependant faire beaucoup mieux: ajoutez une extension à NSTimer qui vous permet de créer un minuteur programmé non avec une cible et un sélecteur, mais avec une fermeture.
Si vous modifiez la portée du bloc en classe et non en fonction et maintenez une référence à la fermeture.
Vous pouvez invoquer cette fermeture avec une fonction. dans la classe. Ainsi, vous pouvez invoquer cette fermeture en tant que sélecteur.
Quelque chose comme ça:
class Test: NSObject {
let backToOriginalBackground = {
}
func backgroundChange() {
NSTimer.scheduledTimerWithTimeInterval(0.5, target: self, selector: #selector(test), userInfo: nil, repeats: false)
}
func test() {
self.backToOriginalBackground()
}
}
Donc, ma réponse à l'idée d'attribuer une selector
à une closure
d'une manière rapide ressemble à certaines des réponses, mais je pensais partager un exemple concret de la façon dont je l'avais fait dans une extension 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)
Ma solution était de créer une variable de bloc de classe comme:
let completionBlock: () -> () = nil
Créez une méthode qui appelle cette completionBlock:
func completed(){
self.completionBlock!()
}
Et à l’intérieur où je veux placer mon sélecteur comme un bloc que j’ai fait:
func myFunc(){
self.completionBlock = {//what I want to be done}
NSTimer.scheduledTimerWithTimeInterval(0.5, target: self, selector: #selector(Myclass.completed), userInfo: nil, repeats: false)
}