首页 文章

Swift中的@selector()?

提问于
浏览
606

我正试图在 Swift 中创建 NSTimer 但我遇到了一些麻烦 .

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

test() 是同一类中的函数 .


我在编辑器中收到错误:

找不到接受所提供参数的'init'的重载

当我将 selector: test() 更改为 selector: nil 时,错误消失 .

我试过了:

  • selector: test()

  • selector: test

  • selector: Selector(test())

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

22 回答

  • 0

    For swift 3

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

    同一类中的函数声明

    func test()
    {
        // my function
    }
    
  • 78

    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

  • 14

    为了防止其他人遇到与NSTimer相同的问题,其中没有其他答案解决了这个问题,重要的是要提一下,如果你使用的是一个不直接或深层次地从NSObject继承的类(例如,手动创建快速文件),即使指定如下,其他任何答案都不会起作用:

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

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

  • 10

    Swift 4中的选择器:

    button.addTarget(self, action: #selector(buttonTapped(sender:)), for: UIControlEvents.touchUpInside)
    
  • 2
    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()
    
    }
    
  • 5

    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 表达式用于实际存在且可用作选择器的类/方法对(请参阅下面的"Selector availability") . 根据the Swift 2.2+ rules for function-type naming,您也可以根据需要自由地制作功能参考 .

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

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

    • 具有相同基本名称的多个函数可以使用上述syntax for function references(例如 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) .

    一般说明:

    Cases where #selector doesn't work, and naming: 有时您没有函数引用来创建选择器(例如,使用在ObjC运行时中动态注册的方法) . 在这种情况下,您可以从字符串构造 Selector :例如 Selector("dynamicMethod:") - 虽然您丢失了编译器的有效性检查 . 当您这样做时,您需要遵循ObjC命名规则,包括每个参数的冒号( : ) .

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

    请记住, private 符号也不会暴露给运行时 - 您的方法需要至少具有 internal 可见性 .

    Key paths: 这些与选择器有关但不完全相同 . 在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的那些语法 .

  • 19

    Swift 4.0

    你创建如下所示的选择器 .

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

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

    功能如下:

    @objc func clickedButton(sender: AnyObject) {
    
    }
    
  • 3

    记下你的位置可能很有用设置触发操作的控件很重要 .

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

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

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

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

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

    如果您想将参数从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

    在调用 selector syntax 的方法中更改为简单的字符串命名

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

    之后,键入func test() .

  • 3
    // 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
    }
    
  • 4

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

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

  • 0

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

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

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

  • 0

    For 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.
    
  • 18

    对于未来的读者,我发现我遇到了一个问题,并且因为将目标 func 标记为私有而导致 unrecognised selector sent to instance 错误 .

    func MUST 是公开可见的,由具有对选择器的引用的对象调用 .

  • 24

    我发现许多这些答案都很有用,但不清楚如何用不是按钮的东西来做这件事 . 我正在快速地向UILabel添加一个手势识别器,并且在阅读以上所有内容后,我发现这对我有用:

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

    “选择器”被声明为:

    func labelTapped(sender: UILabel) { }
    

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

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

    你创建如下所示的选择器 .
    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) {
    }
    
  • 44

    Swift 2.2+ and Swift 3 Update

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

    Selector("keyboardDidHide:")
    

    #selector(keyboardDidHide(_:))
    

    另见:Swift Evolution Proposal

    Note (Swift 4.0):

    如果使用 #selector ,则需要将该函数标记为 @objc

    例:

    @objc func something(_ sender: UIButton)

相关问题