@selector () no Swift?

Estou tentando criar um NSTimer no Swift mas estou tendo alguns problemas.

 NSTimer(timeInterval: 1, target: self, selector: test(), userInfo: nil, repeats: true) 

test() é uma function da mesma class.


Eu recebo um erro no editor:

Não foi possível encontrar uma sobrecarga para o ‘init’ que aceita os argumentos fornecidos

Quando eu mudo o selector: test() para selector: nil o erro desaparece.

Eu tentei:

  • selector: test()
  • selector: test
  • selector: Selector(test())

Mas nada funciona e não consigo encontrar uma solução nas referências.

O Swift em si não usa seletores – vários padrões de projeto que fazem uso de seletores no Objective-C funcionam de maneira diferente no Swift. (Por exemplo, use encadeamento opcional em tipos de protocolo ou is / as testes em vez de respondsToSelector: e use encerramentos sempre que possível, em vez de performSelector: para melhor segurança de tipo / memory.)

Mas ainda há várias APIs importantes baseadas em ObjC que usam seletores, incluindo timers e o padrão target / action. O Swift fornece o tipo Selector para trabalhar com eles. (Swift usa isso automaticamente no lugar do tipo SEL da ObjC.)

No Swift 2.2 (Xcode 7.3) e posterior (incluindo Swift 3 / Xcode 8 e Swift 4 / Xcode 9):

Você pode construir um Selector partir de um tipo de function Swift usando a expressão #selector .

 let timer = Timer(timeInterval: 1, target: object, selector: #selector(MyClass.test), userInfo: nil, repeats: false) button.addTarget(object, action: #selector(MyClass.buttonTapped), for: .touchUpInside) view.perform(#selector(UIView.insertSubview(_:aboveSubview:)), with: button, with: otherButton) 

O melhor desta abordagem? Uma referência de function é verificada pelo compilador Swift, portanto, você pode usar a expressão #selector somente com pares de classs / methods que realmente existem e são elegíveis para uso como seletores (consulte “Disponibilidade do seletor” abaixo). Você também é livre para tornar sua referência de function tão específica quanto você precisar, de acordo com as regras do Swift 2.2+ para nomeação de tipo de function .

(Isso é realmente uma melhoria em relação à diretiva @selector() do @selector() , porque a verificação do -Wundeclared-selector do compilador verifica apenas se o seletor nomeado existe. A referência da function Swift que você passa para #selector verifica a existência, a associação em uma class e a assinatura do tipo .)

Há algumas ressalvas extras para as referências de function que você passa para a expressão #selector :

  • Múltiplas funções com o mesmo nome base podem ser diferenciadas por seus labels de parâmetro usando a syntax mencionada anteriormente para referências de function (por exemplo, insertSubview(_:at:) vs insertSubview(_:aboveSubview:) ). Mas se uma function não tiver parâmetros, a única maneira de desambiguá-la é usar uma conversão com a assinatura do tipo da function (por exemplo, foo as () -> () vs foo(_:) ).
  • Há uma syntax especial para pares de getter / setter de propriedade no Swift 3.0+. Por exemplo, dado um var foo: Int , você pode usar #selector(getter: MyClass.foo) ou #selector(setter: MyClass.foo) .

Notas gerais:

Casos em que #selector não funciona e nomeação: Às vezes você não tem uma referência de function para fazer um seletor com (por exemplo, com methods registrados dinamicamente no runtime ObjC). Nesse caso, você pode construir um Selector partir de uma string: por exemplo, Selector("dynamicMethod:") – embora você perca a verificação de validade do compilador. Quando você faz isso, você precisa seguir as regras de nomenclatura ObjC, incluindo dois pontos (:) para cada parâmetro.

Disponibilidade do seletor: o método referenciado pelo seletor deve ser exposto ao tempo de execução ObjC. No Swift 4, todo método exposto a ObjC deve ter sua declaração precedida pelo atributo @objc . (Nas versões anteriores, você tem esse atributo de graça em alguns casos, mas agora você precisa declará-lo explicitamente.)

Lembre-se de que private símbolos private também não estão expostos ao tempo de execução – seu método precisa ter, pelo menos, visibilidade internal .

Caminhos principais: estão relacionados, mas não exatamente, aos seletores. Há uma syntax especial para estes no Swift 3, por exemplo: chris.valueForKeyPath(#keyPath(Person.friends.firstName)) . Veja SE-0062 para detalhes. E ainda mais coisas do KeyPath no Swift 4 , certifique-se de usar a API correta baseada no KeyPath em vez de seletores, se apropriado.

Você pode ler mais sobre seletores em Interagindo com APIs Objective-C em Usando Swift com Cocoa e Objective-C .

Nota: Antes do Swift 2.2, o Selector estava em conformidade com o StringLiteralConvertible , portanto, você pode encontrar um código antigo no qual as strings nuas são passadas para APIs que usam seletores. Você vai querer executar o “Convert to Current Swift Syntax” no Xcode para obter aqueles usando o #selector .

Veja um exemplo rápido de como usar a class Selector no Swift:

 override func viewDidLoad() { super.viewDidLoad() var rightButton = UIBarButtonItem(title: "Title", style: UIBarButtonItemStyle.Plain, target: self, action: Selector("method")) self.navigationItem.rightBarButtonItem = rightButton } func method() { // Something cool here } 

Observe que, se o método transmitido como uma string não funcionar, ele falhará no tempo de execução, não no tempo de compilation e interromperá seu aplicativo. Seja cuidadoso

Além disso, se a sua class (Swift) não descer de uma class Objective-C, então você deve ter dois pontos no final da string de nome do método de destino e você deve usar a propriedade @objc com seu método de destino, por exemplo

 var rightButton = UIBarButtonItem(title: "Title", style: UIBarButtonItemStyle.Plain, target: self, action: Selector("method")) @objc func method() { // Something cool here } 

caso contrário, você receberá um erro “Seletor não reconhecido” no tempo de execução.

Para os futuros leitores, descobri que tive um problema e estava recebendo um unrecognised selector sent to instance erro de unrecognised selector sent to instance causado pela marcação da func target como private.

O func deve ser publicamente visível para ser chamado por um object com uma referência a um seletor.

Atualização Swift 2.2+ e Swift 3

Use a nova expressão #selector , que elimina a necessidade de usar literais de string, tornando o uso menos propenso a erros. Para referência:

 Selector("keyboardDidHide:") 

torna-se

 #selector(keyboardDidHide(_:)) 

Veja também: Proposta Swift Evolution

Nota (Swift 4.0):

Se usando #selector você precisaria marcar a function como @objc

Exemplo:

@objc func something(_ sender: UIButton)

Apenas no caso de alguém ter o mesmo problema que tive com o NSTimer, onde nenhuma das outras respostas resolveu o problema, é realmente importante mencionar que, se você estiver usando uma class que não herde do NSObject diretamente ou no fundo da hierarquia ( Por exemplo, arquivos swift criados manualmente), nenhuma das outras respostas funcionará mesmo quando especificado da seguinte maneira:

 let timer = NSTimer(timeInterval: 1, target: self, selector: "test", userInfo: nil, repeats: false) func test () {} 

Sem alterar nada além de fazer a class herdar do NSObject, parei de receber o erro “Unrecognized selector” e obtive minha lógica funcionando como esperado.

Swift 4.0

você cria o Seletor como abaixo.

1. Adicione o evento a um botão como:

 button.addTarget(self, action: #selector(clickedButton(sender:)), for: UIControlEvents.touchUpInside) 

e a function será como abaixo:

 @objc func clickedButton(sender: AnyObject) { } 

Se você quiser passar um parâmetro para a function do NSTimer, então aqui está sua solução:

 var somethingToPass = "It worked" let timer = NSTimer.scheduledTimerWithTimeInterval(0.01, target: self, selector: "tester:", userInfo: somethingToPass, repeats: false) func tester(timer: NSTimer) { let theStringToPrint = timer.userInfo as String println(theStringToPrint) } 

Inclua os dois pontos no texto do seletor (testador 🙂 e seu (s) parâmetro (s) vão em userInfo.

Sua function deve usar o NSTimer como parâmetro. Em seguida, basta extrair userInfo para obter o parâmetro passado.

Seletores são uma representação interna de um nome de método em Objective-C. Em Objective-C “@selector (methodName)” iria converter um método de código fonte em um tipo de dados de SEL. Como você não pode usar a syntax @selector no Swift (o rickster está lá), você precisa especificar manualmente o nome do método como um object String diretamente, ou passando um object String para o tipo Seletor. Aqui está um exemplo:

 var rightBarButton = UIBarButtonItem( title: "Logout", style: UIBarButtonItemStyle.Plain, target: self, action:"logout" ) 

ou

 var rightBarButton = UIBarButtonItem( title: "Logout", style: UIBarButtonItemStyle.Plain, target: self, action:Selector("logout") ) 
 // for swift 2.2 // version 1 buttton.addTarget(self, action: #selector(ViewController.tappedButton), forControlEvents: .TouchUpInside) buttton.addTarget(self, action: #selector(ViewController.tappedButton2(_:)), forControlEvents: .TouchUpInside) // version 2 buttton.addTarget(self, action: #selector(self.tappedButton), forControlEvents: .TouchUpInside) buttton.addTarget(self, action: #selector(self.tappedButton2(_:)), forControlEvents: .TouchUpInside) // version 3 buttton.addTarget(self, action: #selector(tappedButton), forControlEvents: .TouchUpInside) buttton.addTarget(self, action: #selector(tappedButton2(_:)), forControlEvents: .TouchUpInside) func tappedButton() { print("tapped") } func tappedButton2(sender: UIButton) { print("tapped 2") } // swift 3.x button.addTarget(self, action: #selector(tappedButton(_:)), for: .touchUpInside) func tappedButton(_ sender: UIButton) { // tapped } button.addTarget(self, action: #selector(tappedButton(_:_:)), for: .touchUpInside) func tappedButton(_ sender: UIButton, _ event: UIEvent) { // tapped } 

Swift 4.1
Com amostra de gesto de toque

 let gestureRecognizer = UITapGestureRecognizer() self.view.addGestureRecognizer(gestureRecognizer) gestureRecognizer.addTarget(self, action: #selector(self.dismiss(completion:))) // Use destination 'Class Name' directly, if you selector (function) is not in same class. //gestureRecognizer.addTarget(self, action: #selector(DestinationClass.dismiss(completion:))) @objc func dismiss(completion: (() -> Void)?) { self.dismiss(animated: true, completion: completion) } 

Veja o documento da Apple para mais detalhes sobre: Expressão do Seletor

 Create Refresh control using Selector method. var refreshCntrl : UIRefreshControl! refreshCntrl = UIRefreshControl() refreshCntrl.tintColor = UIColor.whiteColor() refreshCntrl.attributedTitle = NSAttributedString(string: "Please Wait...") refreshCntrl.addTarget(self, action:"refreshControlValueChanged", forControlEvents: UIControlEvents.ValueChanged) atableView.addSubview(refreshCntrl) 

// Refresh Control Method

 func refreshControlValueChanged(){ atableView.reloadData() refreshCntrl.endRefreshing() } 

Desde que o Swift 3.0 é publicado, é até um pouco mais sutil declarar um alvo.

 class MyCustomView : UIView { func addTapGestureRecognizer() { // the "_" is important let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(MyCustomView.handleTapGesture(_:))) tapGestureRecognizer.numberOfTapsRequired = 1 addGestureRecognizer(tapGestureRecognizer) } // since Swift 3.0 this "_" in the method implementation is very important to // let the selector understand the targetAction func handleTapGesture(_ tapGesture : UITapGestureRecognizer) { if tapGesture.state == .ended { print("TapGesture detected") } } } 

Ao usar performSelector()

/addtarget()/NStimer.scheduledTimerWithInterval() methods seu método (correspondente ao seletor) deve ser marcado como

 @objc For Swift 2.0: { //... self.performSelector(“performMethod”, withObject: nil , afterDelay: 0.5) //... //... btnHome.addTarget(self, action: “buttonPressed:", forControlEvents: UIControlEvents.TouchUpInside) //... //... NSTimer.scheduledTimerWithTimeInterval(0.5, target: self, selector : “timerMethod”, userInfo: nil, repeats: false) //... } @objc private func performMethod() { … } @objc private func buttonPressed(sender:UIButton){ …. } @objc private func timerMethod () { …. } 

Para o Swift 2.2, você precisa escrever ‘#selector ()’ em vez de string e nome do seletor para que as possibilidades de erro de ortografia e travamento devido a isso não estejam mais lá. Abaixo está o exemplo

 self.performSelector(#selector(MyClass.performMethod), withObject: nil , afterDelay: 0.5) 

você cria o Seletor como abaixo.
1

 UIBarButtonItem( title: "Some Title", style: UIBarButtonItemStyle.Done, target: self, action: "flatButtonPressed" ) 

2

 flatButton.addTarget(self, action: "flatButtonPressed:", forControlEvents: UIControlEvents.TouchUpInside) 

Observe que a syntax @selector desapareceu e foi substituída por uma String simples nomeando o método para chamar. Há uma área onde todos podemos concordar que a verbosidade ficou no caminho. Claro, se declaramos que existe um método de destino chamado flatButtonPressed: é melhor escrevermos um:

 func flatButtonPressed(sender: AnyObject) { NSLog("flatButtonPressed") } 

definir o timer:

  var timer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: Selector("flatButtonPressed"), userInfo: userInfo, repeats: true) let mainLoop = NSRunLoop.mainRunLoop() //1 mainLoop.addTimer(timer, forMode: NSDefaultRunLoopMode) //2 this two line is optinal 

Para ser completo, aqui está o flatButtonPressed

 func flatButtonPressed(timer: NSTimer) { } 

Eu achei muitas dessas respostas úteis, mas não estava claro como fazer isso com algo que não era um botão. Eu estava adicionando um reconhecedor de gestos a um UILabel rapidamente e me esforcei, então aqui está o que eu achei que funcionou para mim depois de ler tudo acima:

 let tapRecognizer = UITapGestureRecognizer( target: self, action: "labelTapped:") 

Onde o “Seletor” foi declarado como:

 func labelTapped(sender: UILabel) { } 

Observe que é público e que não estou usando a syntax Selector (), mas é possível fazer isso também.

 let tapRecognizer = UITapGestureRecognizer( target: self, action: Selector("labelTapped:")) 

Usando #selector irá verificar o seu código em tempo de compilation para se certificar de que o método que você deseja chamar realmente existe. Melhor ainda, se o método não existir, você receberá um erro de compilation: o Xcode se recusará a criar seu aplicativo, banindo assim o esquecimento de outra possível fonte de bugs.

 override func viewDidLoad() { super.viewDidLoad() navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .Add, target: self, action: #selector(addNewFireflyRefernce)) } func addNewFireflyReference() { gratuitousReferences.append("Curse your sudden but inevitable betrayal!") } 

Pode ser útil observar onde você configura o controle que aciona a ação.

Por exemplo, descobri que ao configurar um UIBarButtonItem, eu tinha que criar o botão dentro de viewDidLoad ou então eu iria obter uma exceção de seletor não reconhecido.

 override func viewDidLoad() { super.viewDidLoad() // add button let addButton = UIBarButtonItem(image: UIImage(named: "746-plus-circle.png"), style: UIBarButtonItemStyle.Plain, target: self, action: Selector("addAction:")) self.navigationItem.rightBarButtonItem = addButton } func addAction(send: AnyObject?) { NSLog("addAction") } 

Altere como uma simples nomeação de string no método que chama a syntax do seletor

 var timer1 : NSTimer? = nil timer1= NSTimer(timeInterval: 0.1, target: self, selector: Selector("test"), userInfo: nil, repeats: true) 

Depois disso, digite func test ().

Para 3 rápido

 let timer = Timer.scheduledTimer(timeInterval: 0.01, target: self, selector: #selector(self.test), userInfo: nil, repeats: true) 

Declaração de Função na Mesma Classe

 func test() { // my function } 

Para Swift 3

// Código de amostra para criar timer

 Timer.scheduledTimer(timeInterval: 1, target: self, selector: (#selector(updateTimer)), userInfo: nil, repeats: true) WHERE timeInterval:- Interval in which timer should fire like 1s, 10s, 100s etc. [Its value is in secs] target:- function which pointed to class. So here I am pointing to current class. selector:- function that will execute when timer fires. func updateTimer(){ //Implemetation } repeats:- true/false specifies that timer should call again n again.