@select器()在斯威夫特?

我试图在Swift创build一个NSTimer ,但是我遇到了一些麻烦。

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

test()是同一个类中的一个函数。


我在编辑器中遇到错误:

无法find接受提供的参数的“init”的重载

当我改变selector: test() selector: nil无错误消失。

我试过了:

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

但没有任何工作,我不能find一个解决scheme的参考。

Swift 本身不使用select器 – Objective-C中使用select器的几种devise模式在Swift中的工作方式不同。 (例如,在协议types上使用可选的链接,或者使用/ astesting,而不是使用respondsToSelector: performSelector:并使用闭包而不是performSelector:以获得更好的types/内存安全性。

但是仍然有许多重要的基于ObjC的API使用select器,包括定时器和目标/动作模式。 Swift提供了Selectortypes来处理这些types。 (Swift自动使用它来代替ObjC的SELtypes。)

在Swift 2.2(Xcode 7.3)及更高版本(包括Swift 3 / Xcode 8和Swift 4 / Xcode 9)中:

您可以使用#selectorexpression式从Swift函数types构造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) 

这个方法的好处是什么? Swift编译器会检查函数引用,所以只能将#selectorexpression式用于实际存在且可用作select器的类/方法对(请参见下面的“select器可用性”)。 您也可以根据需要自由地创build函数引用,如Swift 2.2+函数types命名规则 。

(这实际上是对ObjC的@selector()指令的一种改进,因为编译器的-Wundeclared-selector检查只validation指定的select器是否存在。传递给#selector的Swift函数引用检查是否存在,类中的成员资格以及types签名。)

对于传递给#selectorexpression式的函数引用还有一些额外的注意事项:

  • 具有相同基本名称的多个函数可以通过参数标签使用前面提到的函数引用语法 (例如insertSubview(_:at:) vs insertSubview(_:aboveSubview:) )来insertSubview(_:aboveSubview:) 。 但是如果一个函数没有参数,那么消除歧义的唯一方法就是使用与函数的types签名(如foo as () -> () vs foo(_:) )进行转换。
  • 在Swift 3.0+中,属性getter / setter对有一个特殊的语法。 例如,给定一个var foo: Int ,你可以使用#selector(getter: MyClass.foo)#selector(setter: MyClass.foo)

一般注意事项:

#selector不起作用的情况,以及命名:有时候你没有一个函数引用来做select器(例如,在ObjC运行时dynamic注册的方法)。 在这种情况下,你可以从一个string构造一个Selector :比如Selector("dynamicMethod:") – 尽pipe你失去了编译器的有效性检查。 当你这样做的时候,你需要遵循ObjC的命名规则,包括每个参数的冒号(:)。

select器可用性:select器引用的方法必须暴露给ObjC运行时。 在Swift 4中,每个暴露给ObjC的方法都必须以@objc属性作为@objc 。 (在以前的版本中,在某些情况下您可以免费获得该属性,但是现在您必须明确声明它。)

请记住, private符号也不会暴露给运行时 – 您的方法至less需要internal可见性。

关键path:这些与select器有关但不完全相同。 在Swift 3中也有一些特殊的语法:例如chris.valueForKeyPath(#keyPath(Person.friends.firstName)) 。 详情请参阅SE-0062 。 在Swift 4中还有更多KeyPath东西 ,所以确保你使用了正确的基于KeyPath的API而不是select器。

你可以阅读更多关于与Cocoa和Objective-C一起使用Swift与 Objective-C API交互的select器。

注意:在Swift 2.2之前, Selector符合StringLiteralConvertible ,因此您可能会发现旧的代码将裸露的string传递给采用select器的API。 你需要在Xcode中运行“Convert to Current Swift Syntax”来获得那些使用#selector

这是一个关于如何在Swift上使用Selector类的简单例子:

 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 } 

请注意,如果作为string传递的方法不起作用,它将在运行时失败,而不是编译时间,并导致应用程序崩溃。 小心

此外,如果您的(Swift)类不从Objective-C类下降,那么您必须在目标方法名称string的末尾添加冒号,并且必须在目标方法中使用@objc属性,例如

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

否则您将在运行时收到“无法识别的select器”错误。

对于未来的读者,我发现我遇到了一个问题,并得到一个unrecognised selector sent to instance错误,是由目标func标记为私人引起的。

func 必须公开可见,被一个对象引用一个select器来调用。

以防万一别人有同样的问题,我曾与NSTimer没有其他答案解决问题,真正重要的是要提到,如果您使用的类不直接或深层次地从NSObjectinheritance(例如手动创build的swift文件),即使指定如下,其他答案也不会有效:

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

没有改变任何东西,只是从NSObject类inheritance我停止得到“无法识别的select器”错误,并得到了我的逻辑按预期工作。

Swift 2.2+和Swift 3更新

使用新的#selectorexpression式,从而消除了使用string文字的必要性,从而减less了使用错误。 以供参考:

 Selector("keyboardDidHide:") 

 #selector(keyboardDidHide(_:)) 

另见: Swift Evolution提案

如果你想从NSTimer传递一个参数给函数,那么这里是你的解决scheme:

 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) } 

在select器文本(testing者:)中包含冒号,并且您的参数在userInfo中。

你的函数应该把NSTimer作为参数。 然后,只需提取userInfo来获取传递的参数。

Swift 4.0

你像下面一样创buildselect器。

1.将事件添加到一个button,如:

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

该function如下:

 func clickedButton(sender: AnyObject) { } 

select器是Objective-C中方法名称的内部表示。 在Objective-C中,“@selector(methodName)”会将源代码方法转换为SEL的数据types。 既然你不能在Swift中使用@selector语法(rickster在那里),你必须直接手动指定方法名作为String对象,或者通过将String对象传递给Selectortypes。 这里是一个例子:

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

要么

 var rightBarButton = UIBarButtonItem( title: "Logout", style: UIBarButtonItemStyle.Plain, target: self, action:Selector("logout") ) 
 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) 

//刷新控制方法

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

由于Swift 3.0已经发布,所以宣布一个适当的targetAction就更加微妙了

 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") } } } 
 // 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 } 

当使用performSelector()

/addtarget()/NStimer.scheduledTimerWithInterval()方法您的方法(匹配select器)应标记为

 @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 () { …. } 

对于Swift 2.2,您需要编写“#selector()”而不是string和select器名称,以避免拼写错误和崩溃的可能性不再存在。 下面是例子

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

我发现许多这些答案是有帮助的,但不清楚如何做到这一点不是一个button。 我正在迅速地将一个手势识别器添加到UILabel中,所以在阅读了上面的所有内容之后,我发现了这些内容:

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

如果“select器”被声明为:

 func labelTapped(sender: UILabel) { } 

请注意,它是公开的,我没有使用Selector()语法,但也可以这样做。

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

使用#selector将在编译时检查您的代码,以确保您想要调用的方法实际存在。 更好的是,如果方法不存在,你将得到一个编译错误:Xcode将拒绝构build你的应用程序,从而消除了另一个可能的错误来源。

 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!") } 

斯威夫特4
用轻拍手势的样本

 let gestureRecognizer = UITapGestureRecognizer() self.view.addGestureRecognizer(gestureRecognizer) gestureRecognizer.addTarget(self, action: #selector(self.dismiss(completion:))) @objc func dismiss(completion: (() -> Void)?) { self.dismiss(animated: true, completion: completion) } 

你像下面一样创buildselect器。
1。

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

2。

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

请注意@selector语法已经不存在了,并用一个简单的string来replace,以便调用该方法。 有一个地方我们都可以同意冗长的方式。 当然,如果我们声明有一个叫做flatButtonPressed的目标方法,我们最好写一个:

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

设置定时器:

  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 

为了完成,这里是flatButtonPressed

 func flatButtonPressed(timer: NSTimer) { } 

注意设置触发操作的控件的位置可能很有用。

例如,我发现设置一个UIBarButtonItem时,我不得不在viewDidLoad中创buildbutton,否则我会得到一个无法识别的select器exception。

 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") } 

更改为调用select器语法的方法中的简单string命名

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

之后,键入func test()。

快3

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

同一类中的函数声明

 func test() { // my function }