3

我正在尝试像这样在运行时为 UI 事件注册回调。

func observeEvent(event: UIControlEvent){
    self.addTarget(self, action: "eventFired:", forControlEvents: event)
}

func eventFired<T>(sender: T){
    print("event fired!")
}

它崩溃并说它在运行时找不到 eventFired:。你能帮忙解决这个问题吗?

4

1 回答 1

1

您不能将泛型函数用作选择器字符串文字的目标。为什么?选择器不包含有关其目标的参数类型的信息,仅:

  • 选择器的目标名称(方法名称),
  • 并且,(隐式)选择器目标的参数数量。

因此,Swift 甚至不允许将具有相同名称和相同数量参数的非泛型函数用作选择器的目标,因为选择器应该使用哪个目标会发生冲突。


例如,考虑以下(游乐场)示例:

class Foo : UIView {
    var button = UIButton()

    init() {
        super.init(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
        button.addTarget(self, action: "eventFired:", forControlEvents: UIControlEvents.TouchUpInside)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    func eventFired(sender: UIButton){
        print("event fired!")
    }
}

let foo = Foo()
foo.button.sendActionsForControlEvents(.TouchUpInside)
    /* event fired! */

这按预期工作,但现在尝试将以下方法添加到Foo类中:

class Foo : UIView { 

    // ...

    func eventFired(nothingToDoWithAButton: Int){
        print("another event fired!")
    }
}

与上面相同的示例现在会产生错误,因为选择器在调用哪个目标方法方面存在冲突。

let foo = Foo()
foo.button.sendActionsForControlEvents(.TouchUpInside)
    /* error ... */

错误:使用 Objective-C 选择器“”的方法“ eventFiredeventFired:与先前使用相同 Objective-C 选择器的声明冲突...

我们最后注意到,如果我们把上面的附加方法改成

class Foo : UIView { 

    // ...

    func eventFired(nothingToDoWithAButton: Int, someBool: Bool){
        print("another event fired!")
    }
}

然后选择器按预期找到它的目标,因为由于参数数量的eventFired(:UIButton)不同,这两种eventFired方法之间可能会有所不同。


从上面可以看出,选择器/选择器文字,以其当前的形式,太原始了,无法使用和明确地识别通用目标。选择器需要包含目标参数类型知识,以便能够在同名/相同数量的参数目标之间进行区分。

最后请注意,当前形式为字符串文字的选择器将在 Swift 2.2 中被弃用,并在 Swift 3.0 中被移除,以支持新的表达式语法#selector,请参阅:


(编辑添加wrt OP:下面的评论)

您可以尝试通过让目标从引用类型 ( ) 中捕获各种信号来模仿一个通用的选择器目标...(:AnyObject),并根据AnyObject参数中包含的类型来处理事件:

class Foo : UIView {
    var button = UIButton()
    var sw = UISwitch()

    init() {
        super.init(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
        button.addTarget(self, action: "eventFired:", forControlEvents: .TouchUpInside)
        sw.addTarget(self, action: "eventFired:", forControlEvents: .ValueChanged)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    func eventFired(sender: AnyObject){
        switch(sender) {
        case is UIButton: print("button event fired!")
        case is UISwitch: print("switch event fired!")
        case _: print("unknown event fired")
        }
    }
}

let foo = Foo()
foo.button.sendActionsForControlEvents(.TouchUpInside)
    /* button event fired! */
foo.sw.sendActionsForControlEvents(.ValueChanged)
    /* switch event fired! */
于 2016-03-07T15:55:13.627 回答