J’essaie de créer un NSTimer
dans Swift
mais j’ai des problèmes.
NSTimer(timeInterval: 1, target: self, selector: test(), userInfo: nil, repeats: true)
test()
est une fonction de la même classe.
Je reçois une erreur dans l’éditeur:
Impossible de trouver une surcharge pour ‘init’ qui accepte les arguments fournis
Lorsque je change de selector: test()
en selector: nil
erreur ne disparaît.
J’ai essayé:
selector: test()
selector: test
selector: Selector(test())
Mais rien ne fonctionne et je ne trouve pas de solution dans les références.
Swift lui – même n’utilise pas de sélecteurs – plusieurs modèles de conception qui, dans Objective-C, utilisent des sélecteurs fonctionnent différemment dans Swift. (Par exemple, utilisez le chaînage facultatif sur les types de protocole ou as
tests / as
place de respondsToSelector:
et utilisez des fermetures partout où vous le pouvez au lieu de performSelector:
pour une sécurité de type / mémoire améliorée.)
Mais il existe encore un certain nombre d’API basées sur ObjC qui utilisent des sélecteurs, y compris des temporisateurs et le modèle cible / action. Swift fournit le type de Selector
pour travailler avec ceux-ci. (Swift l’utilise automatiquement à la place du type SEL
d’ObjC.)
Vous pouvez construire un Selector
partir d’un type de fonction Swift en utilisant l’expression #selector
.
let timer = Timer(timeInterval: 1, target: object, selector: #selector(MyClass.test), userInfo: nil, repeats: false) button.addTarget(object, action: #selector(MyClass.buttonTapped), for: .touchUpInside) view.perform(#selector(UIView.insertSubview(_:aboveSubview:)), with: button, with: otherButton)
La grande chose à propos de cette approche? Une référence de fonction est vérifiée par le compilateur Swift. Vous pouvez donc utiliser l’expression #selector
uniquement avec des paires classe / méthode qui existent réellement et peuvent être utilisées comme sélecteurs (voir «Disponibilité du sélecteur» ci-dessous). Vous êtes également libre de rendre votre référence de fonction aussi spécifique que nécessaire, conformément aux règles de Swift 2.2+ pour le nommage par type de fonction .
(Il s’agit en fait d’une amélioration par rapport à la directive @selector()
d’ @selector()
, car le contrôle -Wundeclared-selector
du compilateur vérifie uniquement l’existence du sélecteur nommé. La référence de fonction Swift que vous transmettez à #selector
vérifie l’existence, l’appartenance à une classe et #selector
signature .)
Il y a quelques mises en garde supplémentaires pour les références de fonction que vous transmettez à l’expression #selector
:
insertSubview(_:at:)
vs insertSubview(_:aboveSubview:)
). Mais si une fonction n’a pas de parameters, la seule façon de le désambiguïser est d’utiliser un cast avec la signature de type de la fonction (par exemple, foo as () -> ()
vs foo(_:)
). var foo: Int
, vous pouvez utiliser #selector(getter: MyClass.foo)
ou #selector(setter: MyClass.foo)
. Cas où #selector
ne fonctionne pas et dénomination: Parfois, vous n’avez pas de référence de fonction pour créer un sélecteur (par exemple, avec des méthodes enregistrées dynamicment dans l’environnement d’exécution ObjC). Dans ce cas, vous pouvez construire un Selector
partir d’une chaîne: par exemple, Selector("dynamicMethod:")
– bien que vous perdiez la vérification de validité du compilateur. Lorsque vous faites cela, vous devez suivre les règles de nommage ObjC, y compris les deux-points (:) pour chaque paramètre.
Disponibilité du sélecteur: La méthode référencée par le sélecteur doit être exposée au runtime ObjC. Dans Swift 4, chaque méthode exposée à ObjC doit avoir sa déclaration @objc
atsortingbut @objc
. (Dans les versions précédentes, vous avez cet atsortingbut gratuitement dans certains cas, mais vous devez maintenant le déclarer explicitement.)
Rappelez-vous que private
symboles private
ne sont pas exposés au runtime également – votre méthode doit avoir au moins internal
visibilité internal
.
Chemins clés: Ils sont liés aux sélecteurs, mais pas tout à fait les mêmes. Il existe également une syntaxe spéciale dans Swift 3: par exemple chris.valueForKeyPath(#keyPath(Person.friends.firstName))
. Voir SE-0062 pour plus de détails. Et encore plus de choses KeyPath
dans Swift 4 , assurez-vous d’utiliser la bonne API basée sur KeyPath plutôt que des sélecteurs, le cas échéant.
Vous pouvez en savoir plus sur les sélecteurs sous Interaction avec les API Objective-C dans Utilisation de Swift avec Cocoa et Objective-C .
Remarque: Avant Swift 2.2, Selector
était conforme à SsortingngLiteralConvertible
, vous pourriez donc trouver un ancien code dans lequel les chaînes nues sont transmises aux API prenant des sélecteurs. Vous voudrez exécuter “Convertir en syntaxe Swift actuelle” dans Xcode pour obtenir ceux utilisant #selector
.
Voici un exemple rapide d’utilisation de la classe Selector
sur Swift:
override func viewDidLoad() { super.viewDidLoad() var rightButton = UIBarButtonItem(title: "Title", style: UIBarButtonItemStyle.Plain, target: self, action: Selector("method")) self.navigationItem.rightBarButtonItem = rightButton } func method() { // Something cool here }
Notez que si la méthode passée en tant que chaîne ne fonctionne pas, elle échouera lors de l’exécution, et non lors de la compilation, et plantera votre application. Faites attention
De plus, si votre classe (Swift) ne descend pas d’une classe Objective-C, vous devez avoir deux points à la fin de la chaîne de nom de la méthode cible et vous devez utiliser la propriété @objc avec votre méthode cible, par exemple
var rightButton = UIBarButtonItem(title: "Title", style: UIBarButtonItemStyle.Plain, target: self, action: Selector("method")) @objc func method() { // Something cool here }
Sinon, vous obtiendrez une erreur “Unrecognized Selector” lors de l’exécution.
Swift 2.2+ et Swift 3 Update
Utilisez la nouvelle expression #selector
, qui élimine le besoin d’utiliser des littéraux de chaîne pour rendre l’utilisation moins sujette aux erreurs. Pour référence:
Selector("keyboardDidHide:")
devient
#selector(keyboardDidHide(_:))
Voir aussi: Proposition Swift Evolution
Note (Swift 4.0):
Si vous utilisez #selector
vous devez marquer la fonction comme @objc
Exemple:
@objc func something(_ sender: UIButton)
Pour les futurs lecteurs, j’ai constaté que j’ai rencontré un problème et que je recevais un unrecognised selector sent to instance
erreur d’ unrecognised selector sent to instance
provoquée par le marquage de la func
cible comme étant privé.
Le func
DOIT être publiquement visible pour être appelé par un object avec une référence à un sélecteur.
Juste au cas où quelqu’un d’autre aurait le même problème avec NSTimer, où aucune des autres réponses ne résout le problème, il est vraiment important de le mentionner si vous utilisez une classe qui n’hérite pas de NSObject directement ou en profondeur dans la hiérarchie ( Par exemple, des fichiers swift créés manuellement), aucune des autres réponses ne fonctionnera même si elle est spécifiée comme suit:
let timer = NSTimer(timeInterval: 1, target: self, selector: "test", userInfo: nil, repeats: false) func test () {}
Sans rien changer d’autre que rendre la classe héritée de NSObject, j’ai cessé de recevoir l’erreur “Unrecognized selector” et ma logique fonctionnait comme prévu.
Swift 4.0
vous créez le sélecteur comme ci-dessous.
1.append l’événement à un bouton comme:
button.addTarget(self, action: #selector(clickedButton(sender:)), for: UIControlEvents.touchUpInside)
et la fonction sera comme ci-dessous:
@objc func clickedButton(sender: AnyObject) { }
Si vous voulez passer un paramètre à la fonction depuis NSTimer, voici votre solution:
var somethingToPass = "It worked" let timer = NSTimer.scheduledTimerWithTimeInterval(0.01, target: self, selector: "tester:", userInfo: somethingToPass, repeats: false) func tester(timer: NSTimer) { let theSsortingngToPrint = timer.userInfo as Ssortingng println(theSsortingngToPrint) }
Incluez les deux points dans le texte du sélecteur (testeur 🙂 et vos parameters vont dans userInfo.
Votre fonction devrait prendre NSTimer comme paramètre. Ensuite, extrayez simplement userInfo pour obtenir le paramètre transmis.
Les sélecteurs sont une représentation interne d’un nom de méthode dans Objective-C. Dans Objective-C, “@selector (methodName)” convertit une méthode de code source en un type de données SEL. Comme vous ne pouvez pas utiliser la syntaxe @selector dans Swift (le rickster y est placé), vous devez spécifier manuellement le nom de la méthode en tant qu’object Ssortingng ou en passant un object Ssortingng au type Selector. Voici un exemple:
var rightBarButton = UIBarButtonItem( title: "Logout", style: UIBarButtonItemStyle.Plain, target: self, action:"logout" )
ou
var rightBarButton = UIBarButtonItem( title: "Logout", style: UIBarButtonItemStyle.Plain, target: self, action:Selector("logout") )
// for swift 2.2 // version 1 buttton.addTarget(self, action: #selector(ViewController.tappedButton), forControlEvents: .TouchUpInside) buttton.addTarget(self, action: #selector(ViewController.tappedButton2(_:)), forControlEvents: .TouchUpInside) // version 2 buttton.addTarget(self, action: #selector(self.tappedButton), forControlEvents: .TouchUpInside) buttton.addTarget(self, action: #selector(self.tappedButton2(_:)), forControlEvents: .TouchUpInside) // version 3 buttton.addTarget(self, action: #selector(tappedButton), forControlEvents: .TouchUpInside) buttton.addTarget(self, action: #selector(tappedButton2(_:)), forControlEvents: .TouchUpInside) func tappedButton() { print("tapped") } func tappedButton2(sender: UIButton) { print("tapped 2") } // swift 3.x button.addTarget(self, action: #selector(tappedButton(_:)), for: .touchUpInside) func tappedButton(_ sender: UIButton) { // tapped } button.addTarget(self, action: #selector(tappedButton(_:_:)), for: .touchUpInside) func tappedButton(_ sender: UIButton, _ event: UIEvent) { // tapped }
Swift 4.1
Avec échantillon de geste
let gestureRecognizer = UITapGestureRecognizer() self.view.addGestureRecognizer(gestureRecognizer) gestureRecognizer.addTarget(self, action: #selector(self.dismiss(completion:))) // Use destination 'Class Name' directly, if you selector (function) is not in same class. //gestureRecognizer.addTarget(self, action: #selector(DestinationClass.dismiss(completion:))) @objc func dismiss(completion: (() -> Void)?) { self.dismiss(animated: true, completion: completion) }
Voir le document d’Apple pour plus de détails sur: Selector Expression
Create Refresh control using Selector method. var refreshCntrl : UIRefreshControl! refreshCntrl = UIRefreshControl() refreshCntrl.tintColor = UIColor.whiteColor() refreshCntrl.atsortingbutedTitle = NSAtsortingbutedSsortingng(ssortingng: "Please Wait...") refreshCntrl.addTarget(self, action:"refreshControlValueChanged", forControlEvents: UIControlEvents.ValueChanged) atableView.addSubview(refreshCntrl)
// Actualiser la méthode de contrôle
func refreshControlValueChanged(){ atableView.reloadData() refreshCntrl.endRefreshing() }
Depuis la publication de Swift 3.0, il est même un peu plus subtil de déclarer une targetAction appropriée
class MyCustomView : UIView { func addTapGestureRecognizer() { // the "_" is important let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(MyCustomView.handleTapGesture(_:))) tapGestureRecognizer.numberOfTapsRequired = 1 addGestureRecognizer(tapGestureRecognizer) } // since Swift 3.0 this "_" in the method implementation is very important to // let the selector understand the targetAction func handleTapGesture(_ tapGesture : UITapGestureRecognizer) { if tapGesture.state == .ended { print("TapGesture detected") } } }
Lors de l’utilisation de performSelector()
/addtarget()/NStimer.scheduledTimerWithInterval()
méthodes votre méthode (correspondant au sélecteur) doit être marquée comme
@objc For Swift 2.0: { //... self.performSelector(“performMethod”, withObject: nil , afterDelay: 0.5) //... //... btnHome.addTarget(self, action: “buttonPressed:", forControlEvents: UIControlEvents.TouchUpInside) //... //... NSTimer.scheduledTimerWithTimeInterval(0.5, target: self, selector : “timerMethod”, userInfo: nil, repeats: false) //... } @objc private func performMethod() { … } @objc private func buttonPressed(sender:UIButton){ …. } @objc private func timerMethod () { …. }
Pour Swift 2.2, vous devez écrire “#selector ()” à la place du nom de la chaîne et du sélecteur afin que les possibilités d’erreur d’orthographe et de blocage ne le soient plus. Ci-dessous, exemple
self.performSelector(#selector(MyClass.performMethod), withObject: nil , afterDelay: 0.5)
vous créez le sélecteur comme ci-dessous.
1.
UIBarButtonItem( title: "Some Title", style: UIBarButtonItemStyle.Done, target: self, action: "flatButtonPressed" )
2.
flatButton.addTarget(self, action: "flatButtonPressed:", forControlEvents: UIControlEvents.TouchUpInside)
Notez que la syntaxe @selector a disparu et est remplacée par une simple chaîne nommant la méthode à appeler. Il y a un domaine où nous pouvons tous convenir que la verbosité a gêné. Bien sûr, si nous déclarons qu’il existe une méthode cible appelée flatButtonPressed: il est préférable d’en écrire une:
func flatButtonPressed(sender: AnyObject) { NSLog("flatButtonPressed") }
régler la timer:
var timer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: Selector("flatButtonPressed"), userInfo: userInfo, repeats: true) let mainLoop = NSRunLoop.mainRunLoop() //1 mainLoop.addTimer(timer, forMode: NSDefaultRunLoopMode) //2 this two line is optinal
Pour être complet, voici le flatButtonPressed
func flatButtonPressed(timer: NSTimer) { }
J’ai trouvé que beaucoup de ces réponses étaient utiles, mais il n’était pas clair comment faire cela avec quelque chose qui n’était pas un bouton. J’étais en train d’append un logiciel de reconnaissance de gestes à un UILabel dans un environnement rapide et difficile. Voici ce que j’ai trouvé après avoir lu tout ce qui précède:
let tapRecognizer = UITapGestureRecognizer( target: self, action: "labelTapped:")
Où le “sélecteur” a été déclaré comme:
func labelTapped(sender: UILabel) { }
Notez qu’il est public et que je n’utilise pas la syntaxe Selector () mais il est possible de le faire également.
let tapRecognizer = UITapGestureRecognizer( target: self, action: Selector("labelTapped:"))
Utiliser #selector vérifiera votre code au moment de la compilation pour vous assurer que la méthode que vous souhaitez appeler existe réellement. Encore mieux, si la méthode n’existe pas, vous obtiendrez une erreur de compilation: Xcode refusera de construire votre application, bannissant ainsi une autre source possible de bogues.
override func viewDidLoad() { super.viewDidLoad() navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .Add, target: self, action: #selector(addNewFireflyRefernce)) } func addNewFireflyReference() { gratuitousReferences.append("Curse your sudden but inevitable betrayal!") }
Il peut être utile de noter où vous configurez le contrôle qui déclenche les actions.
Par exemple, j’ai constaté que lors de la configuration d’un UIBarButtonItem, je devais créer le bouton dans viewDidLoad, sinon je verrais une exception de sélecteur non reconnue.
override func viewDidLoad() { super.viewDidLoad() // add button let addButton = UIBarButtonItem(image: UIImage(named: "746-plus-circle.png"), style: UIBarButtonItemStyle.Plain, target: self, action: Selector("addAction:")) self.navigationItem.rightBarButtonItem = addButton } func addAction(send: AnyObject?) { NSLog("addAction") }
Changer comme une simple chaîne dans la méthode appelant la syntaxe du sélecteur
var timer1 : NSTimer? = nil timer1= NSTimer(timeInterval: 0.1, target: self, selector: Selector("test"), userInfo: nil, repeats: true)
Ensuite, tapez func test ().
Pour swift 3
let timer = Timer.scheduledTimer(timeInterval: 0.01, target: self, selector: #selector(self.test), userInfo: nil, repeats: true)
Déclaration de fonction dans la même classe
func test() { // my function }
Pour Swift 3
// Exemple de code pour créer une timer
Timer.scheduledTimer(timeInterval: 1, target: self, selector: (#selector(updateTimer)), userInfo: nil, repeats: true) WHERE timeInterval:- Interval in which timer should fire like 1s, 10s, 100s etc. [Its value is in secs] target:- function which pointed to class. So here I am pointing to current class. selector:- function that will execute when timer fires. func updateTimer(){ //Implemetation } repeats:- true/false specifies that timer should call again n again.