I searched a lot for selector method in Swift 3, but I have lots of confusion for it.
1) what is difference between Selector & #selector?
2) if I write with Selector, the function is outlined means not available?
3) How to pass a parameter with #selector method.
My code
let button = UIButton()
button.addTarget(self, action: #selector(getData(_:true)), for: .touchUpInside)
button.addTarget(self, action: Selector(), for: .touchUpInside)
func getData(_ isShowing:Bool){
}
Can you help me to clear my confusion?
Thank you for your valuable time
Answers to your questions:
Selector is a type. (to indicate that it's a function type). Whereas #selector is to call a function. #selector --> will return Selector type. #selector checks if there is any function exist with that function name
First answer will clarify this
You can send value through sender like this. Example: button.layer.setValue(forKey:"someKey")
I believe #selector is just a language construction that creates an object of type Selector. You want to use #selector as the compiler actually checks if the method exists anywhere, where with Selector("abc") you just run the constructor and it's not validated.
Related
Not sure why I am getting an "Unrecognized selector sent to class" error when I try to use the button I've created in code.
Here is my code:
let sendButton: UIButton = {
let button = UIButton(type: .system)
button.setTitle("Send", for: .normal)
button.setTitleColor(UIColor.white, for: .normal)
button.backgroundColor = #colorLiteral(red: 0.5098039216, green: 0.5215686275, blue: 0.8392156863, alpha: 1)
button.layer.cornerRadius = 5
button.isUserInteractionEnabled = true
button.addTarget(self, action: #selector(postComment), for: .touchUpInside)
return button
}()
It's actually all a matter of timing. You are declaring an instance property along with a define-and-call function that initializes that property, and you proceed to say button.addTarget(self... in that function. The question then is: when will that function's code run? Incredibly, that matters a lot, so much so that it actually changes the meaning of self.
When you say let to make this declaration, the code runs at a time when the instance does not yet exist. The instance is exactly what we are in the process of forming at that moment; it isn't "cooked" yet. In fact, unbeknownst to you, self at that moment means the UIButton class! Therefore the resulting target-action is invalid; you have no class method postComment; it's an instance method (rightly). And so later on when you tap the button and we try to say postComment to the class, we crash. "Unrecognized selector sent to class", exactly as you say.
On the other hand, when you say lazy var, the code is not called until after the instance self does exist, self means the instance, and all is well.
It's terribly confusing to beginners, and to not-so-beginners; even after pointing out the problem, I've proceeded to make exactly the same mistake myself. In my opinion the compiler should catch this and stop you, but it doesn't. I've filed a bug: http://bugs.swift.org/browse/SR-4865
I actually Just figured it out.
For some reason, replacing 'let' with 'lazy var' seems to do the trick. I can't explain why it works, but it does.
This question already has answers here:
Passing closure in swift as parameter to be used by selector in function
(2 answers)
Closed 5 years ago.
I'm attempting to pass in a function to add as a target to a UIButton in Swift3, but I'm getting the following error:
Argument of '#selector' does not refer to an '#objc' method, property, or initializer
Here's my code:
func updateButtonAction(_ action: () -> (), button: UIButton) {
button.addTarget(self, action: #selector(customFunction(action)), for: .touchUpInside)
}
func customFunction(_ function: () -> ()) {
function()
}
I'm wrapping a passed in function in another function so I know the class that owns it.
I think the issue is that first class functions in Swift aren't supported in Objective-C and the selector keyword only uses Objective-C functions.
Does anyone know a way around this?
Selector is just a fancy synonym for method name. It's not a closure or a function. It's the name of a method on an Obj-C object. Technically, a selector is a string.
customFunction(action) is not a selector. The selector is customFunction(_:).
You are trying to pass an implicit parameter to the selector. That's not how selectors work. You are not calling the method, you are just telling the UIButton which method it should call - UIButton is calling the method and the button is also specifying the parameters.
If you need to pass some code to the event handler, save it on your instance.
var function: (() -> Void)?
func customFunction() {
self.function?()
}
In general it's a good idea to have only one event handler and not change it.
Also note that your example has no need for first class functions.
I want to use a selector, but I need to pass in the arguments
I understand the syntax follows:
#selector(class.method(_:paramName:))
but i need to actually pass in parameters. How do I do this?
Here's my attempt:
exploreTap = UITapGestureRecognizer(target: self, action: #selector(MainViewController.showViewWithIdentifier(_:exploreView,id:"explore")))
You cannot pass parameters to selectors, selector is just a method name, nothing else. You are not calling the method so you cannot pass parameters.
Just call the necessary code inside your tap handler.
func onTap() {
MainViewController.showViewWithIdentifier(exploreView, id:"explore")
}
and then
UITapGestureRecognizer(target: self, action: #selector(onTap))
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:
why can't I no longer pass simply the function name String to the action?
how is the proper way to implement this following the Swift Way? Using the Selector class?
why do we need to pass the #objc keyword and how it affects the function?
Thank you!
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.
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)
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.
Well, it is called evolution
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.
I found out that the #objc is needed only when the function is declared as private or the object doesn't inherit from NSObject
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.
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
}
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.
Programmatically
button.addTarget(self, action: #selector(returnAction), for: .touchUpInside)
// MARK: - Action
#objc private func returnAction(sender: UIButton) {
print(sender.tag)
}
I have the following code:
button.addTarget(self, action: "buttonIsPressed:", forControlEvents: .TouchDown)
Why do I need the ":" after the string for action?
It comes from Objective-C. Basically it means that the action method takes a parameter. In your case the parameter passed will be the sender (i.e. the UIButton that generated the action to be called.
because your function buttonIsPressed has 1 parameter.
one : equal to one parameter
U dont need it. But then you have to have function that takes no parameters.
func buttonIsPressed(){
println("button pressed")
}