我正在尝试在 Swift
中创建一个 NSTimer
,但遇到了一些问题。
NSTimer(timeInterval: 1, target: self, selector: test(), userInfo: nil, repeats: true)
test()
是同一类中的一个函数。
我在编辑器中收到错误:
找不到接受提供的参数的“init”的重载
当我将 selector: test()
更改为 selector: nil
时,错误消失了。
我试过了:
选择器:测试()
选择器:测试
选择器:选择器(测试())
但是没有任何效果,我在参考文献中找不到解决方案。
selector: test()
将调用 test
并将其返回值传递给 selector
参数。
Swift 本身不使用选择器——在 Objective-C 中使用选择器的几种设计模式在 Swift 中的工作方式不同。 (例如,在协议类型或 is
/as
测试上使用可选链而不是 respondsToSelector:
,并尽可能使用闭包而不是 performSelector:
以获得更好的类型/内存安全性。)
但仍有许多重要的基于 ObjC 的 API 使用选择器,包括计时器和目标/动作模式。 Swift 提供了 Selector
类型来处理这些。 (Swift 自动使用它来代替 ObjC 的 SEL
类型。)
在 Swift 2.2 (Xcode 7.3) 及更高版本(包括 Swift 3 / Xcode 8 和 Swift 4 / Xcode 9)中:
您可以使用 #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:) 与 insertSubview(_:aboveSubview:)),可以通过参数标签区分具有相同基本名称的多个函数。但是如果一个函数没有参数,唯一可以消除歧义的方法是使用带有函数类型签名的 as 强制转换(例如 foo as () -> () vs foo(_:))。
Swift 3.0+ 中的属性 getter/setter 对有一个特殊的语法。例如,给定一个 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 与 Cocoa 和 Objective-C 结合使用中的 Interacting with Objective-C APIs 下阅读有关选择器的更多信息。
注意: 在 Swift 2.2 之前,Selector
符合 StringLiteralConvertible
,因此您可能会发现旧代码将裸字符串传递给采用选择器的 API。您需要在 Xcode 中运行“转换为当前 Swift 语法”以获取使用 #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
}
请注意,如果作为字符串传递的方法不起作用,它将在运行时失败,而不是在编译时失败,并使您的应用程序崩溃。当心
@selector
很方便,但并未像您想象的那样正式执行。 “未声明的选择器”只是来自编译器的警告,因为总是可以在运行时引入新的选择器。不过,Swift 中的可验证/可重构选择器引用将是 a good feature request to make。
此外,如果您的 (Swift) 类不是来自 Objective-C 类,那么您必须在目标方法名称字符串的末尾有一个冒号,并且您必须将 @objc 属性与您的目标方法一起使用,例如
var rightButton = UIBarButtonItem(title: "Title", style: UIBarButtonItemStyle.Plain, target: self, action: Selector("method"))
@objc func method() {
// Something cool here
}
否则您将在运行时收到“无法识别的选择器”错误。
Selector
关键字不是强制性的。所以在这种情况下签名必须是 @objc func method(timer: NSTimer) {/*code*/}
@objc
为我工作。我不需要在方法签名中包含 timer: NSTimer
即可调用它。
Swift 2.2+ 和 Swift 3 更新
使用新的 #selector
表达式,它消除了使用字符串文字的需要,从而减少了使用错误的可能性。以供参考:
Selector("keyboardDidHide:")
变成
#selector(keyboardDidHide(_:))
注意(斯威夫特 4.0):
如果使用 #selector
,您需要将函数标记为 @objc
例子:
@objc func something(_ sender: UIButton)
斯威夫特 4.0
您创建如下所示的选择器。
1.将事件添加到一个按钮,如:
button.addTarget(self, action: #selector(clickedButton(sender:)), for: UIControlEvents.touchUpInside)
功能如下:
@objc func clickedButton(sender: AnyObject) {
}
@objc
放在 func
之前,这在 Swift 4 中是必需的。
对于未来的读者,我发现我遇到了一个问题,并且遇到了一个 unrecognised selector sent to instance
错误,这是由于将目标 func
标记为私有而导致的。
func
必须 公开可见,以便由引用选择器的对象调用。
objc
。例如:@objc private func foo() { ...
那么您可以随意使用 "foo"
作为选择器
internal
,因此不指定任何访问修饰符。我经常使用这种模式://MARK: - Selector Methods\n extension MyController {\n func buttonPressed(_ button: UIButton) {
以防万一其他人遇到与 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 以获取传递的参数。
scheduledTimerWith...
会自动将其添加到当前运行循环中 - 所以这里根本没有奇怪的行为;)
选择器是 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")
)
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")
}
Objective-C 选择器
选择器标识一个方法。
//Compile time
SEL selector = @selector(foo);
//Runtime
SEL selector = NSSelectorFromString(@"foo");
例如
[object sayHello:@"Hello World"];
//sayHello: is a selector
selector
是来自 Objective-C
世界的一个词,您可以从 Swift
使用它来有可能从 Swift
调用 Objective-C
它允许您在运行时执行一些代码
Swift 2.2
之前的语法是:
Selector("foo:")
由于函数名称作为 String
参数(“foo”)传递到 Selector
,因此无法在 编译时检查名称/强>。因此,您可以得到一个运行时错误:
unrecognized selector sent to instance
Swift 2.2+
之后的语法是:
#selector(foo(_:))
Xcode 的自动完成功能帮助您调用正确的方法
在调用选择器语法的方法中更改为简单的字符串命名
var timer1 : NSTimer? = nil
timer1= NSTimer(timeInterval: 0.1, target: self, selector: Selector("test"), userInfo: nil, repeats: true)
之后,键入 func test()。
正如许多人所说的那样,选择器是一种客观的 c 方法,可以动态调用已被转移到 Swift 的方法,在某些情况下,我们仍然坚持使用它,比如 UIKit,可能是因为他们在 SwiftUI 上工作来替换它,但一些 api 有更多像 Swift Timer 这样的 swift 版本,例如你可以使用
class func scheduledTimer(withTimeInterval interval: TimeInterval,
repeats: Bool,
block: @escaping (Timer) -> Void) -> Timer
相反,你可以这样称呼它
Timer.scheduledTimer(withTimeInterval: 1,
repeats: true ) {
... your test code here
}
或者
Timer.scheduledTimer(withTimeInterval: 1,
repeats: true,
block: test)
其中方法 test 采用 Timer 参数,或者如果您希望 test 采用命名参数
Timer.scheduledTimer(withTimeInterval: 1,
repeats: true,
block: test(timer:))
您还应该使用 Timer
而不是 NSTimer
因为 NSTimer
是旧的 Objective-C 名称
对于斯威夫特 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)
对于迅捷3
let timer = Timer.scheduledTimer(timeInterval: 0.01, target: self, selector: #selector(self.test), userInfo: nil, repeats: true)
函数声明在同一类中:
@objc func test()
{
// my function
}
self
。这应该足够了:let timer = Timer.scheduledTimer(timeInterval: 0.01, target: self, selector: #selector(test), userInfo: nil, repeats: true)
size:andShape:
,如果第一个参数被命名,您可能需要一个With
,即func init(Data data: NSData)
的initWithData: