@selector () dans Swift?

j'essaie de créer un NSTimer dans Swift mais j'ai quelques problèmes.

NSTimer(timeInterval: 1, target: self, selector: test(), userInfo: nil, repeats: true)

test() est une fonction de la même classe.


j'obtiens une erreur de l'éditeur:

N'a pas pu trouver de surcharge pour "init" qui accepte les arguments

quand je change selector: test() en selector: nil l'erreur 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.

593
demandé sur Mihriban Minaz 2014-06-03 09:21:43

21 réponses

Swift lui - même n'utilise pas de sélecteurs-plusieurs modèles de conception qui dans L'objectif-c font usage de sélecteurs fonctionnent différemment dans Swift. (Par exemple , utilisez des chaînes optionnelles sur les types de protocole ou is / as tests au lieu de respondsToSelector: , et utilisez des fermetures partout où vous pouvez au lieu de performSelector: pour une meilleure sécurité de type/mémoire.)

mais il existe encore un certain nombre d'API importantes basées sur L'ObjC qui utilisent des sélecteurs, y compris les minuteries et le schéma cible/action. Swift fournit le type Selector pour travailler avec ceux-ci. (Swift utilise automatiquement cette option à la place du type SEL D'ObjC.)

dans Swift 2.2 (Xcode 7.3) et les versions suivantes (y compris Swift 3 / Xcode 8 et Swift 4 / Xcode 9):

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? Un la référence de la fonction est vérifiée par le compilateur Swift, de sorte que vous pouvez utiliser l'expression #selector seulement avec les paires de classes/méthodes qui existent réellement et qui sont éligibles pour une utilisation 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 vous le souhaitez, comme dans les règles Swift 2.2+ Pour la désignation de type de fonction .

(il s'agit en fait d'une amélioration par rapport à la directive @selector() de L'ObjC, parce que la le contrôle -Wundeclared-selector du compilateur vérifie seulement que le sélecteur nommé existe. La référence de la fonction Swift que vous passez à #selector vérifie l'existence, l'appartenance à une classe et le type de signature.)

il y a quelques mises en garde supplémentaires pour la fonction références que vous passez à l'expression #selector :

  • des fonctions multiples avec le même nom de base peuvent être différenciées par leurs étiquettes de paramètre en utilisant les susnommés syntaxe de la fonction des références (par exemple insertSubview(_:at:) vs insertSubview(_:aboveSubview:) ). Mais si une fonction n'a pas de paramètres, la seule façon de désambiguer est d'utiliser un as moulé avec la signature de type de la fonction (par exemple foo as () -> () vs foo(_:) ).
  • il y a une syntaxe spéciale pour les couples propriété getter/setter dans Swift 3.0+. Par exemple , avec un var foo: Int , vous pouvez utiliser #selector(getter: MyClass.foo) ou #selector(setter: MyClass.foo) .

remarques Générales:

cas où #selector ne fonctionne pas, et appellation: parfois, vous n'avez pas de référence de fonction pour faire un sélecteur avec (par exemple, avec des méthodes dynamiquement enregistrées dans L'exécution ObjC). Dans ce cas, vous pouvez construire un Selector à partir d'une chaîne de caractères: 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 colons ( : ) pour chaque paramètre.

disponibilité du sélecteur: la méthode référencée par le sélecteur doit être exposée à L'exécution ObjC. Dans Swift 4, chaque méthode exposée à ObjC doit avoir sa déclaration préfacée avec l'attribut @objc . (Dans les versions précédentes, vous avez cet attribut gratuitement dans certains cas, mais maintenant vous devez le déclarer explicitement.)

rappelez - vous que les symboles private ne sont pas exposés le moteur d'exécution, votre méthode doit avoir au moins internal visibilité.

chemins de Clé: Ces sont liées, mais pas tout à fait les mêmes que les sélecteurs. Il y a une syntaxe spéciale pour ceux-ci dans Swift 3, aussi: par exemple chris.valueForKeyPath(#keyPath(Person.friends.firstName)) . Voir SE-0062 pour plus de détails. Et encore plus KeyPath stuff in Swift 4 , alors assurez-vous que vous utilisez la bonne API basée sur le chemin de clavier au lieu de sélecteurs si approprié.

vous pouvez en savoir plus sur les sélecteurs sous interagissant avec L'objectif-c APIs dans utilisant Swift avec le cacao et L'objectif-c .

Note: avant Swift 2.2, Selector conforme à StringLiteralConvertible , de sorte que vous pourriez trouver un vieux code où des cordes nues sont passées aux API qui prennent des sélecteurs. Vous voulez lancer "Convert to Current Swift syntaxe" dans Xcode pour obtenir ceux qui utilisent #selector .

874
répondu rickster 2017-09-26 20:40:54

voici un exemple rapide sur la façon d'utiliser 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 chaîne ne fonctionne pas, elle échouera à l'exécution, ne compilera pas le temps, et écrasera votre application.

78
répondu Oscar Swanros 2014-06-03 05:31:39

aussi, si votre classe (Swift) ne descend pas D'une classe Objective-C, vous devez avoir un deux-points à la fin de la chaîne de noms de 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 de" sélecteur non reconnu " à l'exécution.

44
répondu user3771857 2015-03-08 12:49:00

pour les futurs lecteurs, j'ai trouvé que j'avais un problème et que je recevais un unrecognised selector sent to instance erreur causée par le fait que la cible func a été marquée "private".

le func doit être visible publiquement pour être appelé par un objet avec une référence à un sélecteur.

23
répondu Rob Sanders 2015-05-10 19:16:16

Swift 2.2+ et Swift 3 Mise à jour

utilisez la nouvelle expression #selector , qui élimine la nécessité d'utiliser des chaînes de caractères littérales rendant l'utilisation moins sujette aux erreurs. Pour référence:

Selector("keyboardDidHide:")

devient

#selector(keyboardDidHide(_:))

Voir aussi: Swift Évolution Proposition

Note (Swift 4.0):

si vous utilisez #selector vous devez marquer la fonction @objc

exemple:

@objc func something(_ sender: UIButton)

23
répondu Kyle Clegg 2017-11-22 05:31:22

juste au cas où quelqu'un d'autre aurait le même problème que J'ai eu avec NSTimer où aucune des autres réponses n'a réglé le problème, il est vraiment important de mentionner que, si vous utilisez une classe qui n'hérite pas de NSObject directement ou profondément dans la hiérarchie(par exemple les 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 de faire hériter la classe de NSObject j'ai arrêté d'avoir L'erreur" Unrecognized selector " et j'ai fait fonctionner ma logique comme prévu.

19
répondu Martin Cazares 2015-05-31 06:59:20

Swift 4.0

vous créez le sélecteur comme ci-dessous.

1.ajouter l'événement à un bouton:

button.addTarget(self, action: #selector(clickedButton(sender:)), for: UIControlEvents.touchUpInside)

et la fonction sera comme ci-dessous:

@objc func clickedButton(sender: AnyObject) {

}
17
répondu pravin salame 2018-03-15 17:50:04

si vous voulez passer un paramètre à la fonction du NSTimer alors 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 theStringToPrint = timer.userInfo as String
    println(theStringToPrint)
}

inclue les deux points dans le texte du sélecteur (testeur:), et vos paramètres vont dans userInfo.

votre fonction devrait prendre NSTimer comme paramètre. Ensuite, il suffit d'extraire userInfo pour obtenir le paramètre passé.

14
répondu Scooter 2014-08-07 15:29:05

sélecteurs sont une représentation interne d'un nom de méthode dans objectif-C. Dans objectif-c" @selector(methodName) " convertirait une méthode de code source en un type de données de SEL. Puisque vous ne pouvez pas utiliser la syntaxe @selector dans Swift (rickster est sur le point là), vous devez spécifier manuellement le nom de la méthode comme un objet String directement, ou en passant un objet String 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")
)
10
répondu Jon Tsiros 2014-06-05 05:43:45

Swift 4.1

avec échantillon de geste du robinet

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

8
répondu Krunal 2018-04-17 10:18:53
// 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
}
6
répondu John Lee 2017-07-28 06:01:00
Create Refresh control using Selector method.   
    var refreshCntrl : UIRefreshControl!
    refreshCntrl = UIRefreshControl()
    refreshCntrl.tintColor = UIColor.whiteColor()
    refreshCntrl.attributedTitle = NSAttributedString(string: "Please Wait...")
    refreshCntrl.addTarget(self, action:"refreshControlValueChanged", forControlEvents: UIControlEvents.ValueChanged)
    atableView.addSubview(refreshCntrl)

//Actualisation De La Méthode De Contrôle

func refreshControlValueChanged(){
    atableView.reloadData()
    refreshCntrl.endRefreshing()

}
5
répondu Renish Dadhaniya 2014-07-19 06:26:29

depuis la publication de Swift 3.0, il est même un peu plus subtil de déclarer une action cible 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")
        }
    }
}
5
répondu LukeSideWalker 2016-11-20 00:17:04

Lorsqu'on utilise performSelector()

/addtarget()/NStimer.scheduledTimerWithInterval() Méthodes votre méthode (correspondant au sélecteur) doit porter la mention

@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 () ' au lieu du nom de la chaîne et du sélecteur pour que les possibilités d'erreurs d'orthographe et de plantage dues à cela ne soient plus là. Voici l'exemple

self.performSelector(#selector(MyClass.performMethod), withObject: nil , afterDelay: 0.5)
4
répondu sschunara 2016-06-01 11:59:38

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 a été remplacée par une chaîne simple nommant la méthode à appeler. Il y a un domaine où nous pouvons tous convenir que la verbosité a entravé le chemin. Bien sûr, si nous avons déclaré qu'il y a une méthode cible appelée flatButtonPressed: nous ferions mieux d'en écrire une:

func flatButtonPressed(sender: AnyObject) {
  NSLog("flatButtonPressed")
}

régler la minuterie:

    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) {
}
3
répondu Daxesh Nagar 2014-07-11 19:20:22

j'ai trouvé bon nombre de ces réponses utiles, mais il n'était pas clair comment le faire avec quelque chose qui n'était pas un bouton. J'ajoutais un reconnaisseur de gestes à un UILabel de swift et j'ai lutté, alors voici ce que j'ai trouvé qui a fonctionné pour moi 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 suit:

func labelTapped(sender: UILabel) { }

notez que c'est public et que je n'utilise pas la syntaxe Selector() mais il est possible de le faire aussi.

let tapRecognizer = UITapGestureRecognizer(
            target: self,
            action: Selector("labelTapped:"))
3
répondu cynistersix 2016-02-14 17:58:04

en utilisant # selector vérifiera votre code au moment de la compilation pour s'assurer que la méthode que vous voulez 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 d'oublier 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!")
    }
3
répondu Swift Developer 2016-05-20 11:31:33

Il peut être utile de noter où vous configurez le contrôle qui déclenche l'action de questions.

par exemple, j'ai trouvé que lors de la mise en place d'un UIBarButtonItem, je devais créer le bouton dans viewDidLoad sinon j'obtiendrais une exception de sélecteur non reconnu.

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")
    }
2
répondu Michael Peterson 2014-07-12 19:33:11

Changement comme une simple chaîne de nommage dans l'appel de méthode pour sélecteur de syntaxe

var timer1 : NSTimer? = nil
timer1= NSTimer(timeInterval: 0.1, target: self, selector: Selector("test"), userInfo: nil, repeats: true)

après cela, type func test ().

1
répondu Thar Htet 2014-10-14 04:49:05

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
} 
0
répondu Mustajab Jafry 2017-02-28 06:41:12

Pour Swift 3

//Exemple de code pour créer de la minuterie

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.
0
répondu CrazyPro007 2018-05-19 17:03:46