'#selector' se refiere a un método que no está expuesto a Objective-C

105

El nuevo Xcode 7.3 que pasa el parámetro a través de addTarget generalmente funciona para mí, pero en este caso arroja el error en el título. ¿Algunas ideas? Lanza otro cuando intento cambiarlo a @objc

¡Gracias!

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

El selector está llamando

func didTapCommentButton(post: Post) {
}
Echizzle
fuente
3
¿Cómo se ve la línea de declaración de clase de FeedViewController? ¿Cómo se declara didTapCommentButton? ¿Qué error obtienes cuando agregas @objc?
vacawama
1
Actualización, edité mi publicación. Estoy lejos de la computadora en la que está encendida en este momento, así que olvido el mensaje de error exacto, pero fue una de esas situaciones en las que XCode me dice que lo agregue y luego arroja un error en su propia decisión.
Echizzle
2
¿Tu clase declara @objc, o es una subclase de NSObject?
NRitH
2
¿Puedes intentar quitar los paréntesis? Es inusual considerando que no debería llamar a una función en un selector.
DanielEdrisian
Esto resolvió mi problema en un segundo http://stackoverflow.com/a/36963058/1685165
Darko

Respuestas:

173

En mi caso la función del selector fue private. Una vez que eliminé privateel error, desapareció. Lo mismo vale fileprivate.

En Swift 4
, deberá agregar @objca la declaración de función. Hasta Swift 4, esto se infirió implícitamente.

Shaked Sayag
fuente
2
Además de fileprivate.
hstdt
gran captura @shaked
jbouaziz
@hstdt, así que si lo configura, fileprivate¿se resolverá?
Hemang
2
@Hemang, no, @hstdt significa que ni privateni fileprivatefuncionará
Gobe
Hacer que la función sea dinámica es más apropiado que eliminar private / fileprivate.
Boon
57

Necesitas usar el @objc atributo on didTapCommentButton(_:)para usarlo #selector.

Dice que hizo eso, pero recibió otro error. Supongo que el nuevo error es que Postno es un tipo compatible con Objective-C. Solo puede exponer un método a Objective-C si todos sus tipos de argumentos y su tipo de retorno son compatibles con Objective-C.

Podrías arreglar eso haciendo Postuna subclase de NSObject, pero eso no va a importar, porque el argumento de didTapCommentButton(_:)no será de Posttodos modos. El argumento de una función de acción es el remitente de la acción, y ese remitente será commentButton, que presumiblemente es un UIButton. Deberías declarar didTapCommentButtonasí:

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

Luego, enfrentará el problema de obtener el Postbotón correspondiente al tocado. Hay varias formas de conseguirlo. Aquí hay uno.

Entiendo (ya que su código dice cell.commentButton) que está configurando una vista de tabla (o una vista de colección). Y dado que su celda tiene una propiedad no estándar nombrada commentButton, supongo que es una UITableViewCellsubclase personalizada . Así que supongamos que su celda se PostCelldeclara así:

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

    // other stuff...
}

Luego, puede subir la jerarquía de vistas desde el botón para encontrar PostCelly obtener la publicación de él:

@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
fuente
¿Si quiero usarlo con función global? @objc can only be used with members of classes, @objc protocols, and concrete extensions of classes
TomSawyer
No puede usarlo con una función global.
Rob Mayoff
8

Intente hacer que el selector apunte a una función contenedora, que a su vez llama a su función delegada. Eso funcionó para mí.

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

-

func wrapperForDidTapCommentButton(post: Post) {
     FeedViewController.didTapCommentButton(post)
}
pfj
fuente
1
¡Trabajó para mi! todavía no estoy seguro de por qué esto es necesario, pero lo aceptaré.
Paul Lehn
0

Como sabes, selector[About] dice que se Objective-Cdebe usar el tiempo de ejecución. Declaraciones que están marcadas como privateo fileprivateno están expuestas al tiempo de ejecución de Objective-C de forma predeterminada . Por eso tienes dos variantes:

  1. Marque su declaración privateo fileprivatecon @objc[Acerca de]
  2. Uso internal, public, openmodificador de acceso [Acerca de]
yoAlex5
fuente