在 Swift 中使用选择器的正确方法

Proper way to use selectors in Swift

我正在以编程方式创建一个视图,并添加一个函数以便该操作响应 UIControlEvents.TouchUpInside 事件:

button.addTarget(self, action: action, forControlEvents: 
UIControlEvents.TouchUpInside)

因此,通过查看文档,我将此操作添加为选择器:

#selector(ViewController.onRegularClick)

XCode接着投诉:

Argument of #selector refers to a method that is not exposed to Objective-C

所以我必须设置处理函数:

@objc func onRegularClick(sender: UIButton)

有人可以通过引导我阅读文档来让这个菜鸟走上正确的方向,或者甚至给出一个简短的解释,在:

  1. 为什么我不能再简单地将函数名称字符串传递给操作?
  2. 遵循 Swift 方式的正确方法是什么?使用选择器 class?
  3. 为什么我们需要传递 @objc 关键字以及它如何影响函数?

谢谢!

  1. 嗯,这叫进化
  2. 当方法中有一些参数时,应该将选择器声明为:

    let selector = #selector(YourClass.selector(_:))
    

    如果选择器与调用者相同 class,您只能输入 #selector(selector(_:))_:表示接受一个参数。所以,如果它接受更多的参数,你应该做这样的事情:(_:, _:)等等。

  3. 我发现 @objc 只有当函数声明为私有或对象不继承自 NSObject

  4. 时才需要

1:目前可以,但它会产生弃用警告。在 Swift 3 这将是一个错误,所以你应该尽快修复它。这个做完了 因为编译器无法检查仅使用 String if 该函数确实存在,如果它是一个有效的 Objective C 函数 可以在运行时动态解析。

2:这样做:

button.addTarget(self, action: #selector(MyViewControllerClass.buttonPressed(_:)), forControlEvents: UIControlEvents.TouchUpInside)

3: 通常你不必使用@objc 属性。我假设您的 class ViewController(出于任何原因)不是从 UIViewController 派生的。如果它派生自 UIViewController 则还继承调用函数选择器所需的 ObjC 行为。

  1. why can't I no longer pass simply the function name String to the action?

不推荐使用字符串作为选择器,您现在应该写 #selector(methodName) 而不是 "methodName"。如果 methodName() 方法不存在,您将得到一个编译错误——另一个 class 的错误在编译时被消除了。这对于字符串是不可能的。

  1. how is the proper way to implement this following the Swift Way? Using the Selector class?

你做对了:

button.addTarget(self, action: #selector(ClassName.methodName(_:)), forControlEvents: UIControlEvents.TouchUpInside)

  1. why do we need to pass the @objc keyword and how it affects the function?

在 Swift 中,通常的方法是在编译时绑定方法的调用和方法的主体(就像 C 和 C++ 那样)。 Objective C 在 运行 时间完成。所以在 Objective C 中你可以做一些在 Swift 中不可能的事情 - 例如可以在 运行 时间交换方法的实现(它被称为方法调配)。 Cocoa 旨在与 Objective C 方法一起使用,这就是为什么您必须通知编译器您的 Swift 方法应该以类似 Objective-C 的方式编译。如果您的 class 继承了 NSObject ,即使没有 @objc 关键字,它也会被编译成类似 ObjC 的风格。

对于 swift3.0 就像下面的代码一样:

        yourButton.addTarget(self, action: #selector(yourButtonPressed), for: .touchUpInside)

和您的 ButtonPressed 方法

@IBAction func yourButtonPressed(sender:UIButton) {
    // Do your code here
}

每个人的答案都很完美,但我有更好的方法。希望你们会喜欢。

fileprivate extension Selector {
    static let buttonTapped = 
        #selector(ViewController.buttonTapped(_:))
}
...
button.addTarget(self, action: .buttonTapped, for: .touchUpInside)

此文件中的此处 private 将有助于仅在文件中显示 buttonTapped。

以编程方式

button.addTarget(self, action: #selector(returnAction), for: .touchUpInside)

// MARK: - Action
@objc private func returnAction(sender: UIButton) {
    print(sender.tag)            
}