28

I'm creating a view programatically, and adding a function so the action responds to the UIControlEvents.TouchUpInside event:

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

So, by going into the documentation I've added this action as a selector:

#selector(ViewController.onRegularClick)

XCode then complaints about:

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

So I have to set up the handler function with:

@objc func onRegularClick(sender: UIButton)

Can some one please put this noob on the right direction by guiding me to the documentation, or even give a short explanation, on:

  1. why can't I no longer pass simply the function name String to the action?
  2. how is the proper way to implement this following the Swift Way? Using the Selector class?
  3. why do we need to pass the @objc keyword and how it affects the function?

Thank you!

1

6 Answers 6

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

Using strings for selectors has been deprecated, and you should now write #selector(methodName)instead of "methodName". If the methodName() method doesn't exist, you'll get a compile error – another whole class of bugs eliminated at compile time. This was not possible with strings.

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

You did it the right way:

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?

In Swift the normal approach is to bind method's calls and method's bodies at compile time (like C and C++ do). Objective C does it at run time. So in Objective C you can do some things that are not possible in Swift - for example it is possible to exchange method's implementation at run time (it is called method swizzling). Cocoa was designed to work with Objective C approach and this is why you have to inform the compiler that your Swift method should be compiled in Objective-C-like style. If your class inherits NSObject it will be compiled ObjC-like style even without @objc keyword.

Sign up to request clarification or add additional context in comments.

3 Comments

You can still create selectors from strings even in Swift 4.1. let selector = Selector.init("lalalal")
Or with NSSelectorFromString("selectorName:arg:")
Lucky me, I badly needed the string-based selector to fix a Circular dependency between the Obj-C header and the Swift header. (I mean the bridging headers)
9
  1. Well, it is called evolution
  2. When there are some arguments in the method, you should declare the selector as:

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

    You can type only #selector(selector(_:)) if the selector is in the same class of the caller. _: means that accept one parameter. So, if it accept more parameters, you should do something like: (_:, _:) and so on.

  3. I found out that the @objc is needed only when the function is declared as private or the object doesn't inherit from NSObject

3 Comments

I would dare imagine @objc is also needed if your class isn't in the NSObject hierarchy? Which admittedly will be rare as it's most likely you'll be receiving UI feedback and therefore that you are a UIViewController descendent.
Didn't know that private functions can be selectors with the objc attribute! Great, thanks! +1
At least in Swift 5, you can use #selector for selectors from other classes; for example: #selector(NSSplitViewController.toggleSidebar(_:))
4

1: Currently you can, but it will create a deprecated warning. In Swift 3 this will be an error, so you should fix it soon. This is done because just using a String can not be checked by the compiler if the function really exists and if it is a valid Objective C function which can be resolved dynamically during runtime.

2: Do it in this way:

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

3: Usually you not have to use the @objc attribute. I assume your class ViewController is (for any reason) not derived from UIViewController. If it derives from UIViewController is inherits also the needed ObjC behavior for calling selectors on functions.

Comments

2

For swift3.0 just do like below code :

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

and yourButtonPressed method

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

Comments

1

Everyones's answers are perfect but I have a better approach. Hope you gonna like it.

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

here in this file private will help to show buttonTapped only in file.

Comments

1

Programmatically

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

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.