我想在NSTimer
创建一个Swift
,但我遇到了一些麻烦。
NSTimer(timeInterval: 1, target: self, selector: test(), userInfo: nil, repeats: true)
test()
是同一类中的一个函数。
我在编辑器中收到错误:
找不到接受提供的参数的'init'的重载
当我将selector: test()
更改为selector: nil
时,错误消失了。
我试过了:
selector: test()
selector: test
selector: Selector(test())
但没有任何作用,我在参考文献中找不到解决方案。
Swift本身不使用选择器 - 在Objective-C中使用选择器的几种设计模式在Swift中的工作方式不同。 (例如,使用协议类型的可选链接或is
/ as
测试而不是respondsToSelector:
,并在任何地方使用闭包而不是performSelector:
以获得更好的类型/内存安全性。)
但是仍然有许多基于ObjC的重要API使用选择器,包括定时器和目标/动作模式。 Swift提供Selector
类型来处理这些。 (Swift自动使用它来代替ObjC的SEL
类型。)
您可以使用Selector
表达式从Swift函数类型构造#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编译器检查,因此您只能将#selector
表达式用于实际存在且可用作选择器的类/方法对(请参阅下面的“选择器可用性”)。根据the Swift 2.2+ rules for function-type naming,您也可以根据需要自由选择函数。
(这实际上是对ObjC的@selector()
指令的改进,因为编译器的-Wundeclared-selector
检查只验证指定的选择器是否存在。传递给#selector
的Swift函数引用检查存在,类的成员资格和类型签名。)
对于传递给#selector
表达式的函数引用,还有一些额外的注意事项:
insertSubview(_:at:)
vs insertSubview(_:aboveSubview:)
)来区分。但是如果函数没有参数,则消除歧义的唯一方法是使用带有函数类型签名的as
强制转换(例如foo as () -> ()
vs foo(_:)
)。var foo: Int
,您可以使用#selector(getter: MyClass.foo)
或#selector(setter: MyClass.foo)
。#selector
不起作用的情况和命名:有时您没有函数引用来创建选择器(例如,在ObjC运行时中动态注册的方法)。在这种情况下,您可以从字符串构造Selector
:例如Selector("dynamicMethod:")
- 虽然你失去了编译器的有效性检查。当您这样做时,您需要遵循ObjC命名规则,包括每个参数的冒号(:
)。
选择器可用性:选择器引用的方法必须公开给ObjC运行时。在Swift 4中,暴露给ObjC的每个方法都必须以@objc
属性开头。 (在以前的版本中,在某些情况下,您可以免费获得该属性,但现在您必须明确声明它。)
请记住,private
符号也不会暴露给运行时 - 您的方法需要至少具有internal
可见性。
关键路径:这些与选择器相关但不完全相同。在Swift 3中也有一些特殊的语法:例如chris.valueForKeyPath(#keyPath(Person.friends.firstName))
。有关详细信息,请参阅SE-0062。甚至更多KeyPath
stuff in Swift 4,所以请确保您使用正确的基于KeyPath的API而不是选择器(如果适用)。
您可以在使用Swift with Cocoa和Objective-C的Interacting with Objective-C APIs下阅读有关选择器的更多信息。
注意:在Swift 2.2之前,Selector
符合StringLiteralConvertible
,因此您可能会发现旧代码将裸字符串传递给采用选择器的API。您将要在Xcode中运行“转换为当前Swift语法”以获取使用#selector
的那些语法。
Swift 4.1 随着轻拍手势的样本
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)
}
有关更多详细信息,请参阅Apple的文档:Selector Expression
// 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
}
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")
}
}
}
使用performSelector()
时
/addtarget()/NStimer.scheduledTimerWithInterval()
方法你的方法(匹配选择器)应该标记为
@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()'而不是字符串和选择器名称,因此拼写错误和崩溃的可能性将不再存在。以下是示例
self.performSelector(#selector(MyClass.performMethod), withObject: nil , afterDelay: 0.5)
你创建如下所示的选择器。 1。
UIBarButtonItem(
title: "Some Title",
style: UIBarButtonItemStyle.Done,
target: self,
action: "flatButtonPressed"
)
2.
flatButton.addTarget(self, action: "flatButtonPressed:", forControlEvents: UIControlEvents.TouchUpInside)
请注意,@ selector语法已经消失,并替换为命名要调用的方法的简单String。有一个领域我们都可以同意这种冗长的方式。当然,如果我们声明有一个名为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) {
}
我发现其中许多答案都很有用,但不清楚如何用不是按钮的东西来做这件事。我正在快速地向UILabel添加一个手势识别器,并且在阅读以上所有内容后,我发现这对我有用:
let tapRecognizer = UITapGestureRecognizer(
target: self,
action: "labelTapped:")
“选择器”被声明为:
func labelTapped(sender: UILabel) { }
请注意,它是公共的,我没有使用Selector()语法,但也可以这样做。
let tapRecognizer = UITapGestureRecognizer(
target: self,
action: Selector("labelTapped:"))
使用#selector将在编译时检查您的代码,以确保您要调用的方法确实存在。更好的是,如果该方法不存在,您将收到编译错误:Xcode将拒绝构建您的应用程序,因此无法忘记另一个可能的错误来源。
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!")
}
注意设置触发操作的控件的位置可能很有用。
例如,我发现在设置UIBarButtonItem时,我必须在viewDidLoad中创建按钮,否则我将得到一个无法识别的选择器异常。
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")
}
在调用选择器语法的方法中更改为简单的字符串命名
var timer1 : NSTimer? = nil
timer1= NSTimer(timeInterval: 0.1, target: self, selector: Selector("test"), userInfo: nil, repeats: true)
之后,键入func test()。
这是一个关于如何在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
}
请注意,如果作为字符串传递的方法不起作用,它将在运行时失败,而不是编译时间,并使应用程序崩溃。小心
对于Swift 3
//创建计时器的示例代码
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.
Swift 4中的选择器:
button.addTarget(self, action: #selector(buttonTapped(sender:)), for: UIControlEvents.touchUpInside)
对于swift 3
let timer = Timer.scheduledTimer(timeInterval: 0.01, target: self, selector: #selector(self.test), userInfo: nil, repeats: true)
函数声明在同一个类中:
@objc func test()
{
// my function
}
此外,如果您的(Swift)类不是从Objective-C类下降,那么您必须在目标方法名称字符串的末尾添加冒号,并且必须将@objc属性与目标方法一起使用,例如:
var rightButton = UIBarButtonItem(title: "Title", style: UIBarButtonItemStyle.Plain, target: self, action: Selector("method"))
@objc func method() {
// Something cool here
}
否则您将在运行时收到“无法识别的选择器”错误。
Swift 2.2+和Swift 3更新
使用新的#selector
表达式,这消除了使用字符串文字的需要,使得使用不易出错。以供参考:
Selector("keyboardDidHide:")
变
#selector(keyboardDidHide(_:))
注意(Swift 4.0):
如果使用#selector
you需要将函数标记为@objc
例:
@objc func something(_ sender: UIButton)
对于未来的读者,我发现我遇到了一个问题,并且因为将目标unrecognised selector sent to instance
标记为私有而导致func
错误。
func
必须是公开可见的,由一个引用选择器的对象调用。
Swift 4.0
你创建如下所示的选择器。
1.将事件添加到如下按钮:
button.addTarget(self, action: #selector(clickedButton(sender:)), for: UIControlEvents.touchUpInside)
功能如下:
@objc func clickedButton(sender: AnyObject) {
}
为了防止其他人遇到与NSTimer相同的问题,其中没有其他答案解决了这个问题,非常重要的是,如果你使用的是一个不直接或深层次地从NSObject继承的类(例如,手动创建swift文件),即使指定如下,其他任何答案都不会起作用:
let timer = NSTimer(timeInterval: 1, target: self, selector: "test",
userInfo: nil, repeats: false)
func test () {}
除了让类继承自NSObject之外,我没有改变任何其他东西,我停止了“无法识别的选择器”错误并使我的逻辑按预期工作。
如果您想将参数从NSTimer传递给函数,那么这是您的解决方案:
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)
}
在选择器文本(tester :)中包含冒号,您的参数在userInfo中。
您的函数应该将NSTimer作为参数。然后只需提取userInfo即可获取传递的参数。
选择器是Objective-C中方法名称的内部表示。在Objective-C中,“@ selector(methodName)”会将源代码方法转换为SEL的数据类型。由于您无法在Swift中使用@selector语法(rickster就在那里),您必须直接手动将方法名称指定为String对象,或者将String对象传递给Selector类型。这是一个例子:
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")
)