'#selector' fait référence à une méthode qui n'est pas exposée à Objective-C

105

Le nouveau Xcode 7.3 passant le paramètre via addTarget fonctionne généralement pour moi, mais dans ce cas, il jette l'erreur dans le titre. Des idées? Il en jette un autre quand j'essaye de le changer en @objc

Je vous remercie!

cell.commentButton.addTarget(self, action: #selector(FeedViewController.didTapCommentButton(_:)), forControlEvents: UIControlEvents.TouchUpInside)

Le sélecteur qu'il appelle

func didTapCommentButton(post: Post) {
}
Échizzle
la source
3
À quoi ressemble la ligne de déclaration de classe de FeedViewController? Comment didTapCommentButton est-il déclaré? Quelle erreur obtenez-vous lorsque vous ajoutez @objc?
vacawama
1
Mettre à jour, j'ai édité mon message. Je suis loin de l'ordinateur sur lequel il est allumé en ce moment, alors j'oublie le message d'erreur exact, mais c'était l'une de ces situations où XCode me dit de l'ajouter puis lève une erreur de sa propre décision.
Echizzle
2
Votre classe déclare-t-elle @objcou est-ce une sous-classe de NSObject?
NRitH
2
Pouvez-vous essayer de supprimer les parenthèses? C'est inhabituel étant donné que vous ne devriez pas appeler une fonction dans un sélecteur.
DanielEdrisian
Cela a résolu mon problème en un second http://stackoverflow.com/a/36963058/1685165
Darko

Réponses:

173

Dans mon cas, la fonction du sélecteur était private. Une fois que j'ai supprimé privatel'erreur, l'erreur avait disparu. Il en va de même fileprivate.

Dans Swift 4,
vous devrez ajouter @objcà la déclaration de fonction. Jusqu'à Swift 4, cela était implicitement déduit.

Shaked Sayag
la source
2
En plus de fileprivate.
hstdt
great catch @shaked
jbouaziz
@hstdt, donc si vous définissez, fileprivateest-ce que cela sera résolu?
Hemang
2
@Hemang, non, @hstdt signifie que ni privateni fileprivatene fonctionnera
Gobe
Rendre le func avec dynamic est plus approprié que de supprimer private / fileprivate.
Boon
57

Vous devez utiliser l' @objcattribut sur didTapCommentButton(_:)pour l'utiliser avec #selector.

Vous dites que vous avez fait cela, mais vous avez une autre erreur. Je suppose que la nouvelle erreur est que ce Postn'est pas un type compatible avec Objective-C. Vous ne pouvez exposer une méthode à Objective-C que si tous ses types d'argument et son type de retour sont compatibles avec Objective-C.

Vous pouvez résoudre ce problème en créant Postune sous-classe de NSObject, mais cela n'a pas d'importance, car l'argument de didTapCommentButton(_:)ne sera pas un de Posttoute façon. L'argument d'une fonction d'action est l' expéditeur de l'action, et cet expéditeur le sera commentButton, ce qui est vraisemblablement un UIButton. Vous devez déclarer didTapCommentButtoncomme ceci:

@objc func didTapCommentButton(sender: UIButton) {
    // ...
}

Vous serez alors confronté au problème d'obtenir le Postcorrespondant au bouton tapé. Il existe plusieurs façons de l'obtenir. En voici une.

Je suppose (puisque votre code l'indique cell.commentButton) que vous configurez une vue de table (ou une vue de collection). Et comme votre cellule a une propriété non standard nommée commentButton, je suppose que c'est une UITableViewCellsous-classe personnalisée . Supposons donc que votre cellule soit PostCelldéclarée comme ceci:

class PostCell: UITableViewCell {
    @IBOutlet var commentButton: UIButton?
    var post: Post?

    // other stuff...
}

Ensuite, vous pouvez remonter la hiérarchie des vues à partir du bouton pour trouver le PostCell, et en obtenir le message:

@objc func didTapCommentButton(sender: UIButton) {
    var ancestor = sender.superview
    while ancestor != nil && !(ancestor! is PostCell) {
        ancestor = view.superview
    }
    guard let cell = ancestor as? PostCell,
        post = cell.post
        else { return }

    // Do something with post here
}
Rob Mayoff
la source
Si je veux l'utiliser avec la fonction globale? @objc can only be used with members of classes, @objc protocols, and concrete extensions of classes
TomSawyer
Vous ne pouvez pas l'utiliser avec une fonction globale.
rob mayoff le
8

Essayez de faire pointer le sélecteur sur une fonction wrapper, qui à son tour appelle votre fonction déléguée. Cela a fonctionné pour moi.

cell.commentButton.addTarget(self, action: #selector(wrapperForDidTapCommentButton(_:)), forControlEvents: UIControlEvents.TouchUpInside)

-

func wrapperForDidTapCommentButton(post: Post) {
     FeedViewController.didTapCommentButton(post)
}
pfj
la source
1
A travaillé pour moi! Je ne sais toujours pas pourquoi cela est nécessaire mais je vais le prendre!
Paul Lehn
0

Comme vous le savez, selector[About] indique que le Objective-Cruntime doit être utilisé. Déclarations marquées privateou fileprivatenon exposées au runtime Objective-C par défaut . C'est pourquoi vous avez deux variantes:

  1. Marquez votre déclaration privateou fileprivatepar @objc[À propos]
  2. Utilisez internal, public, openmodificateur d'accès [ A propos]
yoAlex5
la source