'#selector' si riferisce a un metodo che non è esposto a Objective-C


105

Il nuovo Xcode 7.3 che passa il parametro tramite addTarget di solito funziona per me, ma in questo caso genera l'errore nel titolo. Qualche idea? Ne lancia un altro quando provo a cambiarlo in @objc

Grazie!

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

Il selettore sta chiamando

func didTapCommentButton(post: Post) {
}

3
Che aspetto ha la riga di dichiarazione della classe di FeedViewController? Come viene dichiarato didTapCommentButton? Quale errore ricevi quando aggiungi @objc?
vacawama

1
Aggiornamento, ho modificato il mio post. Sono lontano dal computer su cui è acceso in questo momento, quindi dimentico il messaggio di errore esatto ma è stata una di quelle situazioni in cui XCode mi dice di aggiungerlo, quindi genera un errore per sua stessa decisione.
Echizzle

2
La tua classe dichiara @objco è una sottoclasse di NSObject?
NRitH

2
Puoi provare a rimuovere le parentesi? È insolito considerando che non dovresti chiamare una funzione in un selettore.
DanielEdrisian

Questo ha risolto il mio problema in un secondo http://stackoverflow.com/a/36963058/1685165
Darko

Risposte:


173

Nel mio caso la funzione del selettore era private. Una volta rimosso privatel'errore, l'errore era sparito. Lo stesso vale per fileprivate.

In Swift 4
dovrai aggiungere @objcalla dichiarazione della funzione. Fino a swift 4 questo era implicitamente dedotto.


2
Oltre a fileprivate.
hstdt

grande cattura @shaked
jbouaziz

@hstdt, quindi se imposti, fileprivateverrà risolto?
Hemang,

2
@Hemang, no, @hstdt significa che né privatefileprivatefunzionerà
Gobe

Rendere il func con dinamico è più appropriato che rimuovere private / fileprivate.
Boon

57

È necessario utilizzare l' @objcattributo su didTapCommentButton(_:)per usarlo con #selector.

Dici di averlo fatto ma hai ricevuto un altro errore. La mia ipotesi è che il nuovo errore sia che Postnon è un tipo compatibile con Objective-C. È possibile esporre un metodo a Objective-C solo se tutti i suoi tipi di argomento e il suo tipo restituito sono compatibili con Objective-C.

Puoi risolverlo creando Postuna sottoclasse di NSObject, ma non avrà importanza, perché l'argomento per didTapCommentButton(_:)non sarà Postcomunque a. L'argomento di una funzione di azione è il mittente dell'azione e tale mittente sarà commentButton, che presumibilmente è un UIButton. Dovresti dichiarare in didTapCommentButtonquesto modo:

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

Dovrai quindi affrontare il problema di ottenere il Postcorrispondente al pulsante toccato. Esistono diversi modi per ottenerlo. Eccone uno.

Capisco (dal momento che il tuo codice dice cell.commentButton) che stai configurando una vista tabella (o una vista raccolta). E poiché la tua cella ha una proprietà non standard denominata commentButton, presumo sia una UITableViewCellsottoclasse personalizzata . Quindi supponiamo che la tua cella sia PostCelldichiarata come questa:

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

    // other stuff...
}

Quindi puoi risalire la gerarchia di visualizzazione dal pulsante per trovare PostCelle ottenere il post da esso:

@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
}

Se voglio usarlo con la funzione globale? @objc can only be used with members of classes, @objc protocols, and concrete extensions of classes
TomSawyer

Non puoi usarlo con una funzione globale.
rob mayoff il

8

Prova a fare in modo che il selettore punti a una funzione wrapper, che a sua volta chiama la tua funzione delegato. Ha funzionato per me.

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

-

func wrapperForDidTapCommentButton(post: Post) {
     FeedViewController.didTapCommentButton(post)
}

1
Ha funzionato per me! ancora non sono sicuro del motivo per cui è necessario ma lo prendo!
Paul Lehn

0

Come sapete selector[About] dice che Objective-Cdovrebbe essere usato il runtime. Dichiarazioni contrassegnate come privateo fileprivatenon esposte al runtime Objective-C per impostazione predefinita . Ecco perché hai due varianti:

  1. Contrassegna la tua dichiarazione privateo fileprivatedi @objc[Informazioni]
  2. Usa internal, public, openaccesso modificatore di [Chi]
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.