„#selector” odnosi się do metody, która nie jest narażona na działanie Objective-C

105

Nowy Xcode 7.3 przekazujący parametr przez addTarget zwykle działa dla mnie, ale w tym przypadku generuje błąd w tytule. Jakieś pomysły? Wyrzuca inny, gdy próbuję zmienić go na @objc

Dziękuję Ci!

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

Selektor, do którego dzwoni

func didTapCommentButton(post: Post) {
}
Echizzle
źródło
3
Jak wygląda wiersz deklaracji klasy FeedViewController? Jak jest deklarowana didTapCommentButton? Jaki błąd pojawia się podczas dodawania @objc?
vacawama
1
Aktualizacja, zredagowałem swój post. Jestem z dala od komputera, na którym jest teraz włączony, więc zapomniałem dokładnego komunikatu o błędzie, ale była to jedna z tych sytuacji, w których XCode mówi mi, żebym go dodał, a następnie wyświetla błąd na własnej decyzji.
Echizzle
2
Czy twoja klasa jest deklarowana @objc, czy jest to podklasa NSObject?
NRitH
2
Czy możesz spróbować usunąć nawiasy? Jest to niezwykłe, biorąc pod uwagę, że nie powinieneś wywoływać funkcji w selektorze.
DanielEdrisian
To rozwiązało mój problem w drugim http://stackoverflow.com/a/36963058/1685165
Darko

Odpowiedzi:

173

W moim przypadku funkcja selektora była private. Po usunięciu privatebłąd zniknął. To samo dotyczy fileprivate.

W Swift 4
będziesz musiał dodać @objcdo deklaracji funkcji. Aż do szybkiego 4 było to dorozumiane.

Shaked Sayag
źródło
2
Oprócz fileprivate.
hstdt
świetny chwyt @shaked
jbouaziz
@hstdt, więc jeśli ustawisz, fileprivateczy problem zostanie rozwiązany?
Hemang
2
@Hemang, nie, @hstdt oznacza, że ​​ani privateani nie fileprivatebędzie działać
Gobe
Tworzenie funkcji z dynamiką jest bardziej odpowiednie niż usuwanie private / fileprivate.
Boon
57

Musisz użyć @objcatrybutu on, didTapCommentButton(_:)aby użyć go z #selector.

Mówisz, że to zrobiłeś, ale pojawił się kolejny błąd. Domyślam się, że nowy błąd Postnie jest typem zgodnym z Objective-C. Metodę można uwidocznić w celu-C tylko wtedy, gdy wszystkie jej typy argumentów i typ zwracany są zgodne z celem-C.

Możesz to naprawić, tworząc Postpodklasę NSObject, ale to nie będzie miało znaczenia, ponieważ argument do didTapCommentButton(_:)i Posttak nie będzie . Argumentem funkcji akcji jest nadawca akcji, a tym nadawcą będzie commentButton, prawdopodobnie UIButton. Powinieneś zadeklarować w didTapCommentButtonten sposób:

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

Będziesz wtedy musiał stawić czoła problemowi uzyskania Postodpowiedniego przycisku. Jest na to wiele sposobów. Tu jest jeden.

Rozumiem (ponieważ twój kod mówi cell.commentButton), że konfigurujesz widok tabeli (lub widok kolekcji). A ponieważ twoja komórka ma niestandardową właściwość o nazwie commentButton, zakładam, że jest to niestandardowa UITableViewCellpodklasa. Załóżmy więc, że twoja komórka jest PostCellzadeklarowana w ten sposób:

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

    // other stuff...
}

Następnie możesz przejść w górę hierarchii widoków od przycisku, aby znaleźć PostCelli pobrać z niego wpis:

@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
źródło
Jeśli chcę go używać z funkcją globalną? @objc can only be used with members of classes, @objc protocols, and concrete extensions of classes
TomSawyer
Nie możesz go używać z funkcją globalną.
rob mayoff
8

Spróbuj ustawić selektor na funkcję opakowującą, która z kolei wywołuje funkcję delegata. To zadziałało dla mnie.

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

-

func wrapperForDidTapCommentButton(post: Post) {
     FeedViewController.didTapCommentButton(post)
}
pfj
źródło
1
Pracował dla mnie! nadal nie jestem pewien, dlaczego jest to konieczne, ale wezmę to!
Paul Lehn
0

Jak wiecie, selector[About] mówi, że Objective-Cpowinno być używane środowisko wykonawcze. Deklaracje, które są domyślnie oznaczone jako privatelub fileprivatenie są widoczne w środowisku uruchomieniowym Objective-C . Dlatego masz dwa warianty:

  1. Oznacz swoją privatelub fileprivatedeklarację przez @objc[Informacje]
  2. Zastosowanie internal, public, openmodyfikator dostęp [O]
yoAlex5
źródło