swiftaddtarget

UIControl addTarget(_:action:for:) Method in Swift 3.2


I have a class, let's call it ClassA, with the following addTarget call.

awesomeBtn.addTarget(nil, action: #selector(awesomeMethod), for: .touchUpInside)

The compiler accepts the above line when method awesomeMethod is in ClassA (i.e. the same class as the addTarget call).

However, if awesomeMethod is NOT in ClassA, let's say it's in ClassB, then the compiler complains and I am forced to specify the class name in the action.

awesomeBtn.addTarget(nil, action: #selector(ClassB.awesomeMethod), for: .touchUpInside)

In previous versions of Swift (not exactly sure which versions), I could simply write the following, regardless of which class contained the method.

awesomeBtn.addTarget(nil, action:("awesomeMethod"), forControlEvents:.touchUpInside)

Would like to understand why this is or whether I am doing something wrong, thanks.


Solution

  • Yes, they changed it from a String where it was simply a runtime error if you mistyped a method name to a #selector that forces a compile time check for the method. They're just trying to find your errors earlier.

    However, if awesomeMethod is NOT in ClassA, let's say it's in ClassB, then the compiler complains and I am forced to specify the class name in the action.

    No, you can specify an @objc protocol that implements the method:

    @objc protocol AwesomeProtocol {
        func awesomeMethod()
    }
    

    Then, even if your class doesn't implement that method, you can specify:

    awesomeBtn.addTarget(nil, action: #selector(AwesomeProtocol.awesomeMethod), for: .touchUpInside)
    

    Note: It doesn't seem to be necessary for anyone to adopt that protocol. The button searches up the responder chain and uses the first matching method that it finds. Although, you should adopt the protocol by any class that implements awesomeMethod so that Swift can detect errors in the method signature at compile time.