How to add a default parameter value in the #selector
One solution is to use a helper function that is separate from the selector
function. The selector function can call the helper function, which can have as many parameters as you wish.
Adding the target:
slider.addTarget(self, action: #selector(handleChange(_ sender: UISlider)), for: .valueChanged)
Target function:
func handleChange(sender: UISlider) {
// logic based on the action or slider
doSomething(isShown: true)
}
Helper function:
func doSomething(isShown: Bool) {
// make your changes using the parameters here
}
Swift Selector with default argument
The thing is that the parameter is not up to you. It is always the button (the "sender"), and that is the only thing it can be.
In other words, if you want this function to have a parameter, then by all means you will need to set your selector string as "cancelClick:"
- the colon means that it takes a parameter. But that parameter must be the button:
func cancelClick(bbi:UIBarButtonItem?) {
However, you will notice that I have cleverly made this UIBarButtonItem parameter an Optional. Why do you think I did that? Because now you can also call it directly and pass nil:
self.cancelClick(nil)
Thus, cancelClick:
now has a way to know whether the call comes from the tapping of a button or by a direct call - if bbi
is not nil, the button was tapped; if bbi
is nil, we were called directly from code. Sneaky, eh?
Another sneaky approach is to make the parameter an AnyObject:
func cancelClick(sender:AnyObject) {
The beauty of this is that you can call it with any kind of class instance. cancelClick
can check the type of the sender
. If it is a UIBarButtonItem (sender is UIBarButtonItem
), then we were called by tapping the button. Otherwise, if called in code, you can pass in a string or anything else that this function might be prepared to deal with.
Why aren't default parameters for functions respected when called via Selector in Swift?
Default parameters are inserted by the Swift compiler when you call the function.
So this is a compile-time thing.
When manually calling the function via a selector, you use the Objective-C runtime, which has no idea about your default parameters.
This is a runtime thing.
Moreover, default parameters don't exist in Objective-C.
You can think of Swift default parameters as a compile-time convenience.
But once you run your code, they're basically gone.
EDIT
If you're looking for a workaround, I can suggest having two different functions, without using default parameters:
@objc func printValue()
{
printValue(value: 7)
}
@objc func printValue(value:Int)
{}
This way, you can call it without arguments through the Objective-C runtime.
Note that when using #selector
, you'll need to cast to resolve ambiguity:
self.button?.addTarget(self, action: #selector((printValue as () -> Void)), for: .touchUpInside)
Swift default parameter & Selector Syntax
You're getting a crash because it expects a different parameter. 'addTarget' expects to get a function who's sole parameter (if any) is AnyObject (or maybe UIView), not Bool
swift functions with default parameters also a selector?
That's right that you are getting sender object which is actually UIBarButtonItem. Have you heard about Target-Action Cocoa pattern? If no, you can read more here:
https://developer.apple.com/library/ios/documentation/General/Conceptual/Devpedia-CocoaApp/TargetAction.html
Especially relevant section to you is "An Action Method Must Have a Certain Form".
Consider to introduce addWebView overload:
func addWebView(sender: NSObject) {
addWebView(url: NSURL(string: "https://www.google.com")!)
}
private func addWebView(url: NSURL) {
//left as is ...
}
Here is update per Dave's comments.
Have to use different name for actual implementation method. Otherwise Swift compiler is failed to resolve the assigned selector name.
Useful code, which demonstrates the problem is attached below:
class Notifier: NSObject {
private var _target: NSObject!
private var _action: Selector!
func addObserver(target: NSObject, action: Selector) {
_target = target
_action = action
}
func invokeMethod() {
guard let t = _target else {
print("target must be set")
return
}
guard let a = _action else {
print("action must be set")
return
}
if t.respondsToSelector(a) {
t.performSelector(a, withObject: self)
}
}
}
class Observer: NSObject {
func subscribe(notifier: Notifier) {
notifier.addObserver(self, action: #selector(Observer.callback))
}
func callback(sender: NSObject) {
callbackImpl(NSURL(string: "https://www.google.com")!)
}
private func callbackImpl(url: NSURL) {
print("url\(url)")
}
}
//client's code
let n = Notifier()
let o = Observer()
o.subscribe(n)
n.invokeMethod()
Swift: Passing a parameter to selector
#selector
describes method signature only. In your case the correct way to initialize the selector is
let selector = #selector(moveToNextTextField(tag:))
Timer has the common target-action mechanism. Target is usually self and action is a method that takes one parameter sender: Timer
. You should save additional data to userInfo
dictionary, and extract it from sender
parameter in the method:
func moveToNextTextField(sender: Timer) {
print(sender.userInfo?["tag"])
}
...
let selector = #selector(moveToNextTextField(sender:))
Timer.scheduledTimer(timeInterval: 0.2, target: self, selector: selector, userInfo: ["tag": 2], repeats: false)
Passing arguments to selector in Swift
It looks like you're misunderstanding a couple of things.
When using target/action, the function signature has to have a certain form…
func doSomething()
or
func doSomething(sender: Any)
or
func doSomething(sender: Any, forEvent event: UIEvent)
where…
The
sender
parameter is the control object sending the action message.
In your case, the sender is the UITapGestureRecognizer
Also, #selector()
should contain the func signature, and does NOT include passed parameters. So for…
func handleTap(sender: UIGestureRecognizer) {
}
you should have…
let gesture = UITapGestureRecognizer(target: self, action: #selector(handleTap(sender:)))
Assuming the func and the gesture are within a view controller, of which modelObj
is a property / ivar, there's no need to pass it with the gesture recogniser, you can just refer to it in handleTap
Swift: Default function parameter value via NotificationCenter
The correct way is
@objc func myFunc(_ notification:Notification) // OR NSNotification also
Don't expect the NotificationCenter.default
to set a value for theFlag: Bool = false
, with
NotificationCenter.default.addObserver(self, selector: #selector(myFunc), name: MyNotificationName, object: nil)
OR
NotificationCenter.default.addObserver(self, selector: #selector(myFunc(_:)), name: MyNotificationName, object: nil)
//
If you want to send a value then insert it inside object
/ userInfo
NotificationCenter.default.post(name:MyNotificationName, object:<#Here#>, userInfo:<#OrHere#>)
Difficulties to assign default value to a parameter of a function
I don't think that is possible. The default value is inserted at the calling site, and therefore needs to be public, see also
Access control in swift 4.
A possible workaround would be to make the parameter optional,
and substitute nil
by the default value locally:
class Foo {
private static let DefaultValue = 10
public func doTask(amount: Int? = nil) {
let amount = amount ?? Foo.DefaultValue
// ...
}
}
Related Topics
Weak Reference to Closure in Swift
Xcode 7 Cast from Xcuielement to Unrelated Type 'string' Always Fails While Fetching JSON
Convert Single File to Swift 3 in Xcode 8
Swift Error "Domain=Nscocoaerrordomain Code=3840 "Invalid Value Around Character 1."
Accessibility Custom Actions Aren't Announced in Swift
Swift Display Image UIimageview
Difference Between Sort and Sortinplace in Swift 2
How to Handle Error with Realm During Writing
Custom Vibration in iOS 8 - Swift
Protocol Associated Type Typealias Assignment Compile Error
How to Retrieve The Name of Participants in Msconversation
How to Cast from Cftyperef to Axuielement in Swift
Nil Cannot Be Assigned to Type Avcapturedeviceinput
Difference Between Firinstanceid.Instanceid().Token() and Messaging.Messaging().Fcmtoken
Swift Can Change Struct Declared with Let If Using an Index But Not If Using a Loop
How to Emit Items, One by One, from Collection with a Delay in Rxswift