Swift中的@selector()?

时间:2014-06-03 05:21:43

标签: swift selector nstimer

我试图在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())

但没有任何作用,我无法在参考文献中找到解决方案。

23 个答案:

答案 0 :(得分:906)

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表达式用于实际存在且可用作选择器的类/方法对(请参阅"选择器可用性"下面) 。根据{{​​3}},您还可以根据需要随意自由地制作功能参考。

(这实际上是对ObjC @selector()指令的改进,因为编译器的-Wundeclared-selector检查仅验证指定的选择器是否存在。您传递给的Swift函数引用#selector检查存在,类中的成员资格以及类型签名。)

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

  • 具有相同基本名称的多个功能可以使用上述the Swift 2.2+ rules for function-type naming(例如insertSubview(_:at:) vs 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))。有关详细信息,请参阅syntax for function references。还有更多SE-0062,因此请确保您使用正确的基于KeyPath的API而不是选择器。

您可以在使用Swift with Cocoa和Objective-C KeyPath stuff in Swift 4下阅读有关选择器的更多信息。

注意:在Swift 2.2之前,Selector符合StringLiteralConvertible,因此您可能会发现旧代码将裸字符串传递给采用选择器的API。您将要运行"转换为当前的Swift语法"在Xcode中使用#selector

答案 1 :(得分:82)

这是一个关于如何在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   
}

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

答案 2 :(得分:46)

此外,如果您的(Swift)类不是从Objective-C类下降,那么您必须在目标方法名称字符串的末尾添加冒号,并且必须将@objc属性与目标方法一起使用,例如< / p>

var rightButton = UIBarButtonItem(title: "Title", style: UIBarButtonItemStyle.Plain, target: self, action: Selector("method"))

@objc func method() {
    // Something cool here   
} 

否则你会得到一个&#34;无法识别的选择器&#34;运行时出错。

答案 3 :(得分:30)

Swift 2.2+和Swift 3 Update

使用新的#selector表达式,这消除了使用字符串文字的需要,使得使用不易出错。供参考:

Selector("keyboardDidHide:")

变为

#selector(keyboardDidHide(_:))

另请参阅:Swift Evolution Proposal

注意(Swift 4.0):

如果使用#selector,您需要将该功能标记为@objc

示例:

@objc func something(_ sender: UIButton)

答案 4 :(得分:23)

对于未来的读者,我发现我遇到了问题,并且收到了标记目标unrecognised selector sent to instance导致的 func 错误作为私人。

func 必须是公开可见的,以便被引用选择器的对象调用。

答案 5 :(得分:21)

Swift 4.0

您可以像下面一样创建选择器。

1.将事件添加到如下按钮:

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

,功能如下:

@objc func clickedButton(sender: AnyObject) {

}

答案 6 :(得分:19)

如果其他人遇到与NSTimer相同的问题,其他答案都没有解决问题,请务必提及,如果你使用的是一个不直接或深入地从NSObject继承的类层次结构(例如手动创建的swift文件),即使按以下方式指定,其他任何答案都不会起作用:

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

除了让类继承自NSObject之外,我没有改变任何其他东西,我停止了#34;无法识别的选择器&#34;错误并让我的逻辑按预期工作。

答案 7 :(得分:14)

如果你想从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即可获得传递的参数。

答案 8 :(得分:10)

选择器是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")
)

答案 9 :(得分:8)

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

答案 10 :(得分:6)

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

答案 11 :(得分:5)

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

}

答案 12 :(得分:5)

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

答案 13 :(得分:4)

使用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)

答案 14 :(得分:3)

您可以像下面一样创建选择器 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) {
}

答案 15 :(得分:3)

我发现其中许多答案都很有帮助,但目前还不清楚如何使用不是按钮的方法来解决这个问题。我正在快速地向UILabel添加一个手势识别器,并且在阅读以上所有内容后,我发现这些功能在我身上发布了:

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

&#34;选择器&#34;被宣布为:

func labelTapped(sender: UILabel) { }

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

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

答案 16 :(得分:3)

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

答案 17 :(得分:2)

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

例如,我发现在设置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")
}

答案 18 :(得分:1)

在调用选择器语法

的方法中更改为简单的字符串命名
var timer1 : NSTimer? = nil
timer1= NSTimer(timeInterval: 0.1, target: self, selector: Selector("test"), userInfo: nil, repeats: true)

之后,键入func test()。

答案 19 :(得分:0)

对于swift 3

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

函数声明在同一个类中:

@objc func test()
{
    // my function
} 

答案 20 :(得分:0)

适用于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.

答案 21 :(得分:0)

Swift 4中的选择器:

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

答案 22 :(得分:0)

selectorObjective-C世界中的一个单词,您可以在Swift中使用它,可以从Objective-C调用Swift

Swift 2.2之前的语法是:

Selector("foo:")

由于函数鬃毛作为字符串参数(“ foo”)传递到Selector中,因此无法在编译时检查名称。结果,您会收到运行时错误:

unrecognized selector sent to instance

Swift 2.2+之后的语法为:

#selector(foo(_:))

Xcode的自动完成功能可帮助您调用正确的方法