“Classname não possui member functionname” ao adicionar o destino UIButton

Super newb no desenvolvimento Swift e iOS aqui.

Estou seguindo este tutorial sobre como implementar um controle personalizado em um aplicativo iOS de visualização única. É um tutorial do Swift 2, mas até agora eu estou fazendo o OK transpondo tudo para 3 enquanto uso (eu uso o XCode 8 Beta).

Eu tenho uma class personalizada, RatingControl , conectada a um View no storyboard.

No construtor da class, eu crio um botão:

 let button = UIButton(frame: CGRect(x: 0, y: 0, width: 44, height: 44)) button.backgroundColor = UIColor.red() 

Então, tento atribuir uma ação ao botão. O tutorial diz que eu deveria fazer assim:

 button.addTarget(self, action: #selector(RatingControl.ratingButtonTapped(_:)), for: .touchDown) 

e então crie, na mesma class RatingControl , o método:

 func ratingButtonTapped(button: UIButton) { print("Button pressed 👍") } 

Mas quando eu compilo, ele reclama:

tipo “RatingControl” não tem membro “ratingButtonTapped”

Eu fiz 100% de certeza que a function está lá, na class, e corretamente nomeada. Fonte completa

Existe algo óbvio que estou perdendo?

O que eu tentei:

  • Adicionado @objc para a definição da class, de acordo com esta resposta (mas isso parece estranho para uma coisa só de Swift, não?)

  • Feito ratingButtonTapped() explicitamente public (mas isso não parece que deveria ser necessário)

  • Fiddled com strings em vez de seletores, button.addTarget(self, action: "RatingControl.ratingButtonTapped", for: .touchDown) e muito mais, mas isso só trava depois.

No Swift 3, a referência de método para: func ratingButtonTapped(button: UIButton) se torna ratingButtonTapped(button:) .

Então, usando #selector(RatingControl.ratingButtonTapped(button:)) também funciona.

E se você quiser manter #selector(RatingControl.ratingButtonTapped(_:)) , então você precisa declarar o método ratingButtonTapped como:

 func ratingButtonTapped(_ button: UIButton) { //<- `_` print("Button pressed 👍") } 

E se você tiver apenas um método ratingButtonTapped na class, você pode endereçar o seletor como #selector(RatingControl.ratingButtonTapped) ou simplesmente (dentro da class #selector(ratingButtonTapped) ) #selector(ratingButtonTapped) .

Isso aconteceu porque o Swift 3 mudou a maneira como ele lida com o nome do primeiro parâmetro. No Swift 3 , todos os nomes de parâmetros devem ser usados ​​ao chamar uma function, a menos que um explícito _ sido declarado como o nome do parâmetro.

O que você usou como seu seletor estava bem se você tivesse declarado sua function como:

 func ratingButtonTapped(_ button: AnyObject) { print("Button pressed 👍") } 

Você também pode ter usado isso como seu seletor:

 #selector(RatingControl.ratingButtonTapped(button:)) 

Adicionado @objc para a definição da class, de acordo com esta resposta (mas isso parece estranho para uma coisa só de Swift, não?)

Seu código pode estar no Swift, mas você está interagindo com o tempo de execução do Objective-C quando está codificando para o Cocoa Touch (framework iOS). O seletor é uma function que precisa estar visível no tempo de execução do Objective-C. Você obtém isso de graça na maioria das vezes porque você está implementando isso em uma class que, por fim, herda de NSObject (como UIViewController ). Se você tiver uma única class Swift que não herda do NSObject , você pode adicionar @objc para tornar a class e os methods visíveis para o tempo de execução do Objective-C.

Se você quiser chamar uma ação que está em seu controlador de visão de uma class diferente, você pode tentar isso.

Use ViewController () para o seu alvo. Use ViewController.functionName para o seu seletor. Não use um método auxiliar para a variável view controller como “vc”, caso contrário você não poderá acessar objects dentro do ViewController.

Aqui está um exemplo de alvo:

 self.addTarget(ViewController(), action:#selector(ViewController.Test(_:)), for: UIControlEvents.touchDragInside) 

No seu View Controller, aqui está um exemplo de ação

 @IBAction func Test(_ sender: Any?) { print("Goodtime was here") 

}

No alvo, você deve adicionar (), mas não no seletor da ação. Você não precisa chamar o @IBAction, ele pode ser apenas func. Algumas pessoas usam @objc ou public qualquer um desses prefixos na ação deve funcionar.

Revise se a ação está em uma Classe ou ViewController diferente, você deve colocar a referência de Classe no destino e no seletor da ação. Caso contrário, ele tentará sempre chamar a ação dentro do mesmo arquivo, independentemente de estar correto no Seletor. Da mesma forma, se a ação estiver no mesmo arquivo use self para o destino e dentro do seletor da ação.

Felicidades

Ao adicionar alvos de botões no 3, primeiro você precisa estar na class .

 class SomeClass { // ... let button = UIButton() // configure button to taste... button.addTarget(self, action: #selector(doSomething), for: .touchUpInside) // ... @objc public func doSomething() { print("hello, world!") } // ... }