I\'m trying to create an NSTimer
in Swift
but I\'m having some trouble.
NSTimer(timeInterval: 1, target: self, selector: test(), us
Since Swift 3.0 is published, it is even a little bit more subtle to declare a targetAction appropriate
class MyCustomView : UIView {
func addTapGestureRecognizer() {
// the "_" is important
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(MyCustomView.handleTapGesture(_:)))
tapGestureRecognizer.numberOfTapsRequired = 1
addGestureRecognizer(tapGestureRecognizer)
}
// since Swift 3.0 this "_" in the method implementation is very important to
// let the selector understand the targetAction
func handleTapGesture(_ tapGesture : UITapGestureRecognizer) {
if tapGesture.state == .ended {
print("TapGesture detected")
}
}
}
Swift 4.1
With sample of tap gesture
let gestureRecognizer = UITapGestureRecognizer()
self.view.addGestureRecognizer(gestureRecognizer)
gestureRecognizer.addTarget(self, action: #selector(self.dismiss(completion:)))
// Use destination 'Class Name' directly, if you selector (function) is not in same class.
//gestureRecognizer.addTarget(self, action: #selector(DestinationClass.dismiss(completion:)))
@objc func dismiss(completion: (() -> Void)?) {
self.dismiss(animated: true, completion: completion)
}
See Apple's document for more details about: Selector Expression
// for swift 2.2
// version 1
buttton.addTarget(self, action: #selector(ViewController.tappedButton), forControlEvents: .TouchUpInside)
buttton.addTarget(self, action: #selector(ViewController.tappedButton2(_:)), forControlEvents: .TouchUpInside)
// version 2
buttton.addTarget(self, action: #selector(self.tappedButton), forControlEvents: .TouchUpInside)
buttton.addTarget(self, action: #selector(self.tappedButton2(_:)), forControlEvents: .TouchUpInside)
// version 3
buttton.addTarget(self, action: #selector(tappedButton), forControlEvents: .TouchUpInside)
buttton.addTarget(self, action: #selector(tappedButton2(_:)), forControlEvents: .TouchUpInside)
func tappedButton() {
print("tapped")
}
func tappedButton2(sender: UIButton) {
print("tapped 2")
}
// swift 3.x
button.addTarget(self, action: #selector(tappedButton(_:)), for: .touchUpInside)
func tappedButton(_ sender: UIButton) {
// tapped
}
button.addTarget(self, action: #selector(tappedButton(_:_:)), for: .touchUpInside)
func tappedButton(_ sender: UIButton, _ event: UIEvent) {
// tapped
}
Using #selector will check your code at compile time to make sure the method you want to call actually exists. Even better, if the method doesn’t exist, you’ll get a compile error: Xcode will refuse to build your app, thus banishing to oblivion another possible source of bugs.
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.rightBarButtonItem =
UIBarButtonItem(barButtonSystemItem: .Add, target: self,
action: #selector(addNewFireflyRefernce))
}
func addNewFireflyReference() {
gratuitousReferences.append("Curse your sudden but inevitable betrayal!")
}
Swift itself doesn't use selectors — several design patterns that in Objective-C make use of selectors work differently in Swift. (For example, use optional chaining on protocol types or is
/as
tests instead of respondsToSelector:
, and use closures wherever you can instead of performSelector:
for better type/memory safety.)
But there are still a number of important ObjC-based APIs that use selectors, including timers and the target/action pattern. Swift provides the Selector
type for working with these. (Swift automatically uses this in place of ObjC's SEL
type.)
You can construct a Selector
from a Swift function type using the #selector
expression.
let timer = Timer(timeInterval: 1, target: object,
selector: #selector(MyClass.test),
userInfo: nil, repeats: false)
button.addTarget(object, action: #selector(MyClass.buttonTapped),
for: .touchUpInside)
view.perform(#selector(UIView.insertSubview(_:aboveSubview:)),
with: button, with: otherButton)
The great thing about this approach? A function reference is checked by the Swift compiler, so you can use the #selector
expression only with class/method pairs that actually exist and are eligible for use as selectors (see "Selector availability" below). You're also free to make your function reference only as specific as you need, as per the Swift 2.2+ rules for function-type naming.
(This is actually an improvement over ObjC's @selector()
directive, because the compiler's -Wundeclared-selector
check verifies only that the named selector exists. The Swift function reference you pass to #selector
checks existence, membership in a class, and type signature.)
There are a couple of extra caveats for the function references you pass to the #selector
expression:
insertSubview(_:at:)
vs insertSubview(_:aboveSubview:)
). But if a function has no parameters, the only way to disambiguate it is to use an as
cast with the function's type signature (e.g. foo as () -> ()
vs foo(_:)
).var foo: Int
, you can use #selector(getter: MyClass.foo)
or #selector(setter: MyClass.foo)
.Cases where #selector
doesn't work, and naming: Sometimes you don't have a function reference to make a selector with (for example, with methods dynamically registered in the ObjC runtime). In that case, you can construct a Selector
from a string: e.g. Selector("dynamicMethod:")
— though you lose the compiler's validity checking. When you do that, you need to follow ObjC naming rules, including colons (:
) for each parameter.
Selector availability: The method referenced by the selector must be exposed to the ObjC runtime. In Swift 4, every method exposed to ObjC must have its declaration prefaced with the @objc
attribute. (In previous versions you got that attribute for free in some cases, but now you have to explicitly declare it.)
Remember that private
symbols aren't exposed to the runtime, too — your method needs to have at least internal
visibility.
Key paths: These are related to but not quite the same as selectors. There's a special syntax for these in Swift 3, too: e.g. chris.valueForKeyPath(#keyPath(Person.friends.firstName))
. See SE-0062 for details. And even more KeyPath stuff in Swift 4, so make sure you're using the right KeyPath-based API instead of selectors if appropriate.
You can read more about selectors under Interacting with Objective-C APIs in Using Swift with Cocoa and Objective-C.
Note: Before Swift 2.2, Selector
conformed to StringLiteralConvertible
, so you might find old code where bare strings are passed to APIs that take selectors. You'll want to run "Convert to Current Swift Syntax" in Xcode to get those using #selector
.
Swift 2.2+ and Swift 3 Update
Use the new #selector
expression, which eliminates the need to use string literals making usage less error-prone. For reference:
Selector("keyboardDidHide:")
becomes
#selector(keyboardDidHide(_:))
See also: Swift Evolution Proposal
Note (Swift 4.0):
If using #selector
you would need to mark the function as @objc
Example:
@objc func something(_ sender: UIButton)