'#selector' refere-se a um método que não é exposto a Objective-C

105

O novo Xcode 7.3 passando o parâmetro via addTarget normalmente funciona para mim, mas neste caso está jogando o erro no título. Alguma ideia? Ele lança outro quando tento alterá-lo para @objc

Obrigado!

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

O seletor está chamando

func didTapCommentButton(post: Post) {
}
Echizzle
fonte
3
Qual é a aparência da linha de declaração de classe do FeedViewController? Como o didTapCommentButton é declarado? Que erro você obtém ao adicionar @objc?
vacawama
1
Atualização, editei minha postagem. Estou longe do computador, ele está ligado agora, então esqueci a mensagem de erro exata, mas foi uma daquelas situações em que o XCode me diz para adicioná-lo e então gera um erro por sua própria decisão.
Echizzle
2
Sua classe declara @objcou é uma subclasse de NSObject?
NRitH
2
Você pode tentar remover os parênteses? É incomum, considerando que você não deve chamar uma função em um seletor.
DanielEdrisian
Isso resolveu meu problema em um segundo http://stackoverflow.com/a/36963058/1685165
Darko

Respostas:

173

No meu caso, a função do seletor era private. Depois de remover o, privateo erro desapareceu. O mesmo vale para fileprivate.

No Swift 4,
você precisará adicionar @objcà declaração da função. Até o swift 4, isso era inferido implicitamente.

Shaked Sayag
fonte
2
Além de fileprivate.
hstdt
ótima captura @shaked
jbouaziz
@hstdt, então se você definir, fileprivateserá resolvido?
Hemang
2
@Hemang, não, @hstdt significa que nem privatenem fileprivatefuncionará
Gobe
Tornar a func com dynamic é mais apropriado do que remover private / fileprivate.
Boon
57

Você precisa usar o @objcatributo em didTapCommentButton(_:)para usá-lo com #selector.

Você diz que fez isso, mas recebeu outro erro. Meu palpite é que o novo erro é que Postnão é um tipo compatível com Objective-C. Você só pode expor um método para Objective-C se todos os seus tipos de argumento, e seu tipo de retorno, forem compatíveis com Objective-C.

Você poderia consertar isso criando Postuma subclasse de NSObject, mas isso não fará diferença, porque o argumento para didTapCommentButton(_:)não será de Postqualquer maneira. O argumento para uma função de ação é o remetente da ação, e esse remetente será commentButton, o que é presumivelmente a UIButton. Você deve declarar didTapCommentButtonassim:

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

Você então enfrentará o problema de obter o Postcorrespondente ao botão tocado. Existem várias maneiras de obtê-lo. Aqui está um.

Percebi (já que seu código diz cell.commentButton) que você está configurando uma visualização de tabela (ou uma visualização de coleção). E como sua célula tem uma propriedade não padrão chamada commentButton, presumo que seja uma UITableViewCellsubclasse personalizada . Então, vamos supor que sua célula seja PostCelldeclarada assim:

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

    // other stuff...
}

Em seguida, você pode subir na hierarquia de visualização a partir do botão para encontrar PostCelle obter a postagem a partir dele:

@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
fonte
Se eu quiser usar com função global? @objc can only be used with members of classes, @objc protocols, and concrete extensions of classes
TomSawyer
Você não pode usá-lo com uma função global.
rob mayoff
8

Tente fazer com que o seletor aponte para uma função wrapper, que por sua vez chama sua função delegada. Isso funcionou para mim.

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

-

func wrapperForDidTapCommentButton(post: Post) {
     FeedViewController.didTapCommentButton(post)
}
pfj
fonte
1
Funcionou para mim! ainda não sei por que isso é necessário, mas vou levá-lo!
Paul Lehn
0

Como você sabe, selector[About] diz que o Objective-Cruntime deve ser usado. Declarações marcadas como privateou fileprivatenão expostas ao tempo de execução Objective-C por padrão . É por isso que você tem duas variantes:

  1. Marque sua declaração privateou fileprivatecomo @objc[Sobre]
  2. Use internal, public, openmodificador de acesso [Sobre]
yoAlex5
fonte