web-dev-qa-db-fra.com

@selector () dans Swift?

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 remplace selector: test() par 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.

639
Arbitur

Swift lui-même n'utilise pas de sélecteurs. Plusieurs modèles de conception qui, dans Objective-C, utilisent les sélecteurs de manière différente dans Swift. (Par exemple, utilisez un chaînage facultatif sur les types de protocole ou les tests is/as au lieu de respondsToSelector:, et utilisez des fermetures chaque fois que vous le pouvez au lieu de performSelector: pour améliorer la sécurité de type/mémoire. .)

Cependant, il existe encore un certain nombre d'API importantes basées sur ObjC qui utilisent des sélecteurs, notamment des minuteries et le modèle cible/action. Swift fournit le type Selector pour travailler avec ceux-ci. (Swift l'utilise automatiquement à la place du type SEL d'ObjC.)

Dans Swift 2.2 (Xcode 7.3) et versions ultérieures (y compris Swift 3/Xcode 8 et Swift 4/Xcode 9):

Vous pouvez construire une Selector à partir d'un type de fonction Swift à l'aide de 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 existantes et pouvant ê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 à le Swift 2.2+ règles de dénomination du type de fonction .

(Il s'agit en réalité d'une amélioration par rapport à la directive @selector() de ObjC, car la vérification -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 dans une classe et tapez signature.)

Il y a quelques mises en garde supplémentaires pour les références de fonction que vous transmettez à l'expression #selector:

  • Plusieurs fonctions avec le même nom de base peuvent être différenciées par leurs étiquettes de paramètre en utilisant la syntaxe susmentionnée pour les références de fonction (par exemple insertSubview(_:at:) vs insertSubview(_:aboveSubview:)). Mais si une fonction n'a pas de paramètre, le seul moyen de la lever de toute ambiguïté consiste à utiliser un transtypage as avec la signature de type de la fonction (par exemple, foo as () -> () vs foo(_:)).
  • Il existe une syntaxe spéciale pour les paires 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).

Notes générales:

Cas où #selector ne fonctionne pas et désignation: Parfois, vous ne disposez pas d'une référence de fonction pour créer un sélecteur (par exemple , avec des méthodes enregistrées dynamiquement dans le runtime 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. Pour ce faire, vous devez suivre les règles de dénomination ObjC, notamment 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 moteur d'exécution ObjC. Dans Swift 4, chaque déclaration de méthode exposée à ObjC doit être précédée de l'attribut @objc. (Dans les versions précédentes, vous aviez cet attribut gratuitement dans certains cas, mais vous devez maintenant le déclarer explicitement.)

N'oubliez pas que les symboles private ne sont pas exposés au runtime également - votre méthode doit avoir au moins la visibilité internal.

Chemins d'accès aux clés: Ils sont liés aux sélecteurs, mais pas tout à fait. Il existe également une syntaxe spéciale pour ces éléments dans Swift 3: par exemple. chris.valueForKeyPath(#keyPath(Person.friends.firstName)). Voir SE-0062 pour plus de détails. Et encore plus KeyPath dans Swift 4 , assurez-vous donc que vous utilisez la bonne API basée sur KeyPath au lieu 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 conforme à StringLiteralConvertible, vous pourrez donc trouver l'ancien code où Les chaînes nues sont transmises aux API qui acceptent les sélecteurs. Vous voudrez exécuter "Syntaxe en Swift courant" dans Xcode pour obtenir ceux qui utilisent #selector.

915
rickster

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 transmise en tant que chaîne ne fonctionne pas, elle échouera au moment de l'exécution, pas à la compilation, et plantera votre application. Faites attention

83
Oscar Swanros

De plus, si votre classe (Swift) ne descend pas d'une classe Objective-C, vous devez avoir un signe deux-points à la fin de la chaîne du 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" au moment de l'exécution.

46
user3771857

Swift 2.2+ et Swift 3 Mise à jour

Utilisez la nouvelle expression #selector, ce qui élimine la nécessité d'utiliser des littéraux de chaîne, ce qui rend l'utilisation moins sujette aux erreurs. Pour référence:

Selector("keyboardDidHide:")

devient

#selector(keyboardDidHide(_:))

Voir aussi: Proposition d'évolution rapide

Remarque (Swift 4.0):

Si vous utilisez #selectorvous devrez marquer la fonction comme @objc

Exemple:

@objc func something(_ sender: UIButton)

31
Kyle Clegg

Pour les futurs lecteurs, j’ai constaté que j’avais un problème et que j’avais une erreur unrecognised selector sent to instance provoquée par marquant la cible func comme privée.

La funcDOIT être visible publiquement pour être appelée par un objet avec une référence à un sélecteur.

23
Rob Sanders

Swift 4.

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

1.ajouter 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) {

}
21
pravin salame

Juste au cas où quelqu'un d'autre aurait le même problème que NSTimer où aucune des autres réponses ne corrige 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, 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 simplement faire en sorte que la classe hérite de NSObject, j'ai cessé de recevoir l'erreur "Unrecognized selector" et ma logique a fonctionné comme prévu.

19
Martin Cazares

Si vous souhaitez transmettre un paramètre à la fonction à partir de 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 theStringToPrint = timer.userInfo as String
    println(theStringToPrint)
}

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

Votre fonction devrait prendre NSTimer en paramètre. Ensuite, extrayez simplement userInfo pour obtenir le paramètre qui a été transmis.

14
Scooter

Les sélecteurs sont une représentation interne d'un nom de méthode dans Objective-C. En Objective-C, "@selector (methodName)" convertirait une méthode de code source en un type de données SEL. Étant donné que vous ne pouvez pas utiliser la syntaxe @selector dans Swift (le rickster y est pointé), vous devez spécifier manuellement le nom de la méthode en tant qu'objet String 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
Jon Tsiros

Swift 4.1
Avec un échantillon de geste de tapotement

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
Krunal
// 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
John Lee
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)

// Actualiser la méthode de contrôle

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

}
5
Renish Dadhaniya

Puisque Swift 3.0 est publié, il est même un peu plus subtil de déclarer une 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
LukeSideWalker

Lorsque vous utilisez 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 de la chaîne et du nom du sélecteur afin que les possibilités d'erreur d'orthographe et de plantage dus à cette erreur n'existent plus. Voici un exemple

self.performSelector(#selector(MyClass.performMethod), withObject: nil , afterDelay: 0.5)
4
sschunara

Utiliser # selector vérifiera votre code lors de la compilation pour vous assurer que la méthode que vous souhaitez appeler existe réellement. Mieux encore, si la méthode n’existe pas, vous obtiendrez une erreur de compilation: Xcode refusera de créer 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!")
    }
3
Swift Developer

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'ajoutais un identificateur de geste à un UILabel dans Swift et je me suis débattu. Voici donc ce que j'ai trouvé qui a fonctionné pour moi après avoir tout lu ci-dessus:

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 aussi.

let tapRecognizer = UITapGestureRecognizer(
            target: self,
            action: Selector("labelTapped:"))
3
cynistersix

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 été remplacée par une simple chaîne nommant la méthode à appeler. Il ya un domaine dans lequel nous pouvons tous convenir que la verbosité est un obstacle. Bien sûr, si nous déclarons qu'il existe une méthode cible appelée flatButtonPressed: nous ferions mieux d'en écrire une:

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

régler le minuteur:

    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
Daxesh Nagar

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

Par exemple, j'ai constaté que lors de la configuration d'un UIBarButtonItem, je devais créer le bouton dans viewDidLoad, sinon j'obtiendrais 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")
}
2
Michael Peterson

Changer comme une simple chaîne de nommage dans la méthode appelant syntaxe de sélecteur

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

Après cela, tapez func test ().

1
Thar Htet

Sélecteur dans Swift 4:

button.addTarget(self, action: #selector(buttonTapped(sender:)), for: UIControlEvents.touchUpInside)
0
Mahesh Chaudhari

Pour Swift

// Exemple de code pour créer un 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.
0
CrazyPro007

Pour Swift

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:

@objc func test()
{
    // my function
} 
0
Mustajab Haider