Swift- error: Variable 'self.___' used before being initialized - ios

I am trying to work with gesture recognizers in a Playground but am having some trouble.
Here is my class:
class foo {
var fooVarSwipe: Any
var fooVarTap: Any
init() {
let gr = UISwipeGestureRecognizer(target: self, action: #selector(foo.bar))
let tr = UITapGestureRecognizer(target: self, action: #selector(foo.tar))
helloApple.addGestureRecognizer(gr)
helloApple.addGestureRecognizer(tr)
helloApple.isUserInteractionEnabled = true
self.fooVarSwipe = gr
self.fooVarTap = tr
}
#objc func tar() {
print("tapped")
}
#objc func bar() {
print("swiped")
currentViewNum = 1
}
}
The problem I am having is that on the line starting with "let gr" it is saying "Variable 'self.fooVarSwipe' used before being initialized." Why is this? I initialize the class outside but it still is showing me the error.
Any help would be much appreciated!!
Cheers and thanks in advance,
Theo

Inside let gr you are targeting self, which is an instance of class foo.
Since you haven't initialised its two variables, compiler throws an error when you try to access them.
Swift doesn't accept this behaviour. I suggest you to declare them as Optional.

Related

Swift property wrapper not compiling any more in Swift 5.4+?

The following code used to work for Swift 5.2, and possibly Swift 5.3.
(Last build was November 2020)
#propertyWrapper
class ActionBindable<Button> where Button : UIControl {
var target: Any? {
didSet { setTargetAction() }
}
weak var wrappedValue: Button! {
didSet { setTargetAction() }
}
private let action: Selector
private let event: UIControl.Event
init(action: Selector, event: UIControl.Event = .touchUpInside) {
self.action = action
self.event = event
}
private func setTargetAction() {
guard target != nil && wrappedValue != nil else { return }
wrappedValue.addTarget(target, action: action, for: event)
}
}
However, I'm getting an error now:
Property type 'UIKit.UIControl?' does not match 'wrappedValue' type 'UIKit.UIControl?'
Haven't been following property wrappers for some time, so I'm wondering what changed.
Here is the code where the property wrapper is being used:
#ActionBindable(action: #selector(addAction))
var addButton: UIControl!
The bug we have to deal with right now is:
When wrappedValue is weak, there is no mechanism to explicitly set a type for a wrapped variable that uses the relevant property wrapper, after its name.
Your workaround is:
// ActionBindable<UIControl>
#ActionBindable(action: #selector(addAction)) var addButton
// Any subclasses:
#ActionBindable<UIButton>(action: #selector(addAction)) var addButton
However, I bet you'll have other problems, because implicitly-unwrapped optionality doesn't propagate outside of property wrappers. I bet it used to, given your addButton definition. You'll probably need a computed variable to wrap addButton then, to avoid future optional checking, which may negate the point of having a property wrapper.

(Swift) How to get rid of "Left side of mutating operator isn't mutable"

complete error:
Left side of mutating operator isn't mutable: 'abc' is a 'let' constant
Happens because I am trying to change value of a variable sent by parameter to function.
Can I get rid of this, or find some other solution?
Code(My code is much complex, but in effect doing the same as this):
func generateABC() {
var abc = "this"
abc += "is"
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap(abc)) )
tapGesture.delegate = self
webView.addGestureRecognizer(tapGesture)
abc += "function"
}
handleTap function :
#objc
func handleTap(_ someString: String) {
someString += "my"
}
Short story: It's impossible to add custom parameters to (any) target/action
Either there is no parameter
#objc
func handleTap() { ...
or the affected recognizer is the parameter
#objc
func handleTap(_ recognizer : UITapGestureRecognizer) { ...
That's it. In both cases the corresponding declaration is
UITapGestureRecognizer(target: self, action: #selector(handleTap))
You have to use a temporary variable to handle the string.
For passing parameters using UITapGestureRecognizer, One approach would be to subClass UITapGestureRecognizer and then set a property as example below:
class SampleGesture: UITapGestureRecognizer {
var someString = String()
}
class ViewController: UIViewController {
let tapGesture = SampleGesture(target: self, action: #selector(self.handleTap))
tapGesture.delegate = self
webView.addGestureRecognizer(tapGesture)
tapGesture.someString = //your text
}
And as for error others already said in answers that Parameters of a function are 'constants' by default
#objc func handleTap(sender: SampleGesture) {
var newTitle: String = sender.someString // you can declare as globally
newTitle += "my"
}
You can not change a Passed variable's value within function. If you want to change value of someString then you have to store it into another variable and use it further like this.
#objc
func handleTap(_ someString: String) {
var newString: String = someString
newString += "my"
}
All parameters passed into a Swift function are constants, so you can’t change them. If you want, you can pass in one or more parameters as inout, which means they can be changed inside your function, and those changes reflect in the original value outside the function.
func doubleInPlace(number: inout Int) {
number *= 2
}
Credit: Paul Hudson https://www.hackingwithswift.com/sixty/5/10/inout-parameters
Be careful if you are going with this approach, because you can end up with unexpected sideffects. You can also simply use a temp variable inside your function, and assign the parameter to it
#objc
func handleTap(_ someString: String)-> String {
var tempString = someString
tempString += "Whatever"
return tempString
}

Passing arguments to selector in Swift

I'm programmatically adding a UITapGestureRecognizer to one of my views:
let gesture = UITapGestureRecognizer(target: self, action: #selector(self.handleTap(modelObj:myModelObj)))
self.imageView.addGestureRecognizer(gesture)
func handleTap(modelObj: Model) {
// Doing stuff with model object here
}
The first problem I encountered was "Argument of '#selector' does not refer to an '#Objc' method, property, or initializer.
Cool, so I added #objc to the handleTap signature:
#objc func handleTap(modelObj: Model) {
// Doing stuff with model object here
}
Now I'm getting the error "Method cannot be marked #objc because the type of the parameter cannot be represented in Objective-C.
It's just an image of the map of a building, with some pin images indicating the location of points of interest. When the user taps one of these pins I'd like to know which point of interest they tapped, and I have a model object which describes these points of interest. I use this model object to give the pin image it's coordinates on the map so I thought it would have been easy for me to just send the object to the gesture handler.
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
Step 1: create the custom object of the sender.
step 2: add properties you want to change in that a custom object of the sender
step 3: typecast the sender in receiving function to a custom object and access those properties
For eg:
on click of the button if you want to send the string or any custom object then
step 1: create
class CustomButton : UIButton {
var name : String = ""
var customObject : Any? = nil
var customObject2 : Any? = nil
convenience init(name: String, object: Any) {
self.init()
self.name = name
self.customObject = object
}
}
step 2-a: set the custom class in the storyboard as well
step 2-b: Create IBOutlet of that button with a custom class as follows
#IBOutlet weak var btnFullRemote: CustomButton!
step 3: add properties you want to change in that a custom object of the sender
btnFullRemote.name = "Nik"
btnFullRemote.customObject = customObject
btnFullRemote.customObject2 = customObject2
btnFullRemote.addTarget(self, action: #selector(self.btnFullRemote(_:)), for: .touchUpInside)
step 4: typecast the sender in receiving function to a custom object and access those properties
#objc public func btnFullRemote(_ sender: Any) {
var name : String = (sender as! CustomButton).name as? String
var customObject : customObject = (sender as! CustomButton).customObject as? customObject
var customObject2 : customObject2 = (sender as! CustomButton).customObject2 as? customObject2
}
Swift 5.0 iOS 13
I concur a great answer by Ninad. Here is my 2 cents, the same and yet different technique; a minimal version.
Create a custom class, throw a enum to keep/make the code as maintainable as possible.
enum Vs: String {
case pulse = "pulse"
case precision = "precision"
}
class customTap: UITapGestureRecognizer {
var cutomTag: String?
}
Use it, making sure you set the custom variable into the bargin. Using a simple label here, note the last line, important labels are not normally interactive.
let precisionTap = customTap(target: self, action: #selector(VC.actionB(sender:)))
precisionTap.customTag = Vs.precision.rawValue
precisionLabel.addGestureRecognizer(precisionTap)
precisionLabel.isUserInteractionEnabled = true
And setup the action using it, note I wanted to use the pure enum, but it isn't supported by Objective C, so we go with a basic type, String in this case.
#objc func actionB(sender: Any) {
// important to cast your sender to your cuatom class so you can extract your special setting.
let tag = customTag as? customTap
switch tag?.sender {
case Vs.pulse.rawValue:
// code
case Vs.precision.rawValue:
// code
default:
break
}
}
And there you have it.
cell.btn.tag = indexPath.row //setting tag
cell.btn.addTarget(self, action: #selector(showAlert(_ :)), for: .touchUpInside)
#objc func showAlert(_ sender: UIButton){
print("sender.tag is : \(sender.tag)")// getting tag's value
}
Just create a custom class of UITapGestureRecognizer =>
import UIKit
class OtherUserProfileTapGestureRecognizer: UITapGestureRecognizer {
let userModel: OtherUserModel
init(target: AnyObject, action: Selector, userModel: OtherUserModel) {
self.userModel = userModel
super.init(target: target, action: action)
}
}
And then create UIImageView extension =>
import UIKit
extension UIImageView {
func gotoOtherUserProfile(otherUserModel: OtherUserModel) {
isUserInteractionEnabled = true
let gestureRecognizer = OtherUserProfileTapGestureRecognizer(target: self, action: #selector(self.didTapOtherUserImage(_:)), otherUserModel: otherUserModel)
addGestureRecognizer(gestureRecognizer)
}
#objc internal func didTapOtherUserImage(_ recognizer: OtherUserProfileTapGestureRecognizer) {
Router.shared.gotoOtherUserProfile(otherUserModel: recognizer.otherUserModel)
}
}
Now use it like =>
self.userImageView.gotoOtherUserProfile(otherUserModel: OtherUserModel)
You can use an UIAction instead:
self.imageView.addAction(UIAction(identifier: UIAction.Identifier("imageClick")) { [weak self] action in
self?.handleTap(modelObj)
}, for: .touchUpInside)
that may be a terrible practice but I simply add whatever I want to restore to
button.restorationIdentifier = urlString
and
#objc func openRelatedFact(_ sender: Any) {
if let button = sender as? UIButton, let stringURL = factButton.restorationIdentifier, let url = URL(string: stringURL) {
if UIApplication.shared.canOpenURL(url) {
UIApplication.shared.open(url, options: [:])
}
}
}

Referring to self during property declaration in Swift

I'm trying to declare and initialize a property with the following code.
class ClassName: UIViewController {
  private let doneButtonItem = UIBarButtonItem(title: "Done", style: UIBarButtonItemStyle.Plain, target: self, action: "doneButtonDidTapped")
func doneButtonDidTapped() {
println("Ulala!")
}
}
However, I got the following error.
Cannot find an initializer for type 'UIBarButtonItem' that accepts an argument list of type '(title: String, style: UIBarButtonItemStyle, target: ClassName -> () -> ClassName, action: String)'
Anybody know what's going on here? Should I give up my attempts to initialize the property inline with the declaration and do the initialization on init() method instead?
As #giorashc says due to swift's 2-phase initialization, self is not yet initalized so you cannot do it.
But I think you could create a lazy inialization:
lazy private var doneButtonItem : UIBarButtonItem = {
[unowned self] in
return UIBarButtonItem(title: "Done", style:UIBarButtonItemStyle.Plain, target: self, action: "doneButtonDidTapped")
}()
The closure in #agy's answer is unnecessary, you can just do (in Swift 3):
lazy var button:UIBarButtonItem = UIBarButtonItem(title: "Title", style: .plain, target: self, action: #selector(buttonPressed(_:)))
Due to swift's 2-phase initialization you need to initialize the parent class before you can use self in the inheriting class.
In your implementation self is yet to be initialized by the parent class so as you said you should move it to the init method of your view controller and create the button after calling the parent's initialization method

What is the syntax for selector parameters in Swift? [duplicate]

I'm trying to create an NSTimer in Swift but I'm having some trouble.
NSTimer(timeInterval: 1, target: self, selector: test(), userInfo: nil, repeats: true)
test() is a function in the same class.
I get an error in the editor:
Could not find an overload for 'init' that accepts the supplied
arguments
When I change selector: test() to selector: nil the error disappears.
I've tried:
selector: test()
selector: test
selector: Selector(test())
But nothing works and I can't find a solution in the references.
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.)
In Swift 2.2 (Xcode 7.3) and later (including Swift 3 / Xcode 8 and Swift 4 / Xcode 9):
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:
Multiple functions with the same base name can be differentiated by their parameter labels using the aforementioned syntax for function references (e.g. 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(_:)).
There's a special syntax for property getter/setter pairs in Swift 3.0+. For example, given a var foo: Int, you can use #selector(getter: MyClass.foo) or #selector(setter: MyClass.foo).
General notes:
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.
Here's a quick example on how to use the Selector class on Swift:
override func viewDidLoad() {
super.viewDidLoad()
var rightButton = UIBarButtonItem(title: "Title", style: UIBarButtonItemStyle.Plain, target: self, action: Selector("method"))
self.navigationItem.rightBarButtonItem = rightButton
}
func method() {
// Something cool here
}
Note that if the method passed as a string doesn't work, it will fail at runtime, not compile time, and crash your app. Be careful
Also, if your (Swift) class does not descend from an Objective-C class, then you must have a colon at the end of the target method name string and you must use the #objc property with your target method e.g.
var rightButton = UIBarButtonItem(title: "Title", style: UIBarButtonItemStyle.Plain, target: self, action: Selector("method"))
#objc func method() {
// Something cool here
}
otherwise you will get a "Unrecognised Selector" error at runtime.
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 #selectoryou would need to mark the function as #objc
Example:
#objc func something(_ sender: UIButton)
Swift 4.0
you create the Selector like below.
1.add the event to a button like:
button.addTarget(self, action: #selector(clickedButton(sender:)), for: UIControlEvents.touchUpInside)
and the function will be like below:
#objc func clickedButton(sender: AnyObject) {
}
For future readers, I found that I experienced a problem and was getting an unrecognised selector sent to instance error that was caused by marking the target func as private.
The func MUST be publicly visible to be called by an object with a reference to a selector.
Just in case somebody else have the same problem I had with NSTimer where none of the other answers fixed the issue, is really important to mention that, if you are using a class that do not inherits from NSObject either directly or deep in the hierarchy(e.g. manually created swift files), none of the other answers will work even when is specified as follows:
let timer = NSTimer(timeInterval: 1, target: self, selector: "test",
userInfo: nil, repeats: false)
func test () {}
Without changing anything else other than just making the class inherit from NSObject I stopped getting the "Unrecognized selector" Error and got my logic working as expected.
If you want to pass a parameter to the function from the NSTimer then here is your solution:
var somethingToPass = "It worked"
let timer = NSTimer.scheduledTimerWithTimeInterval(0.01, target: self, selector: "tester:", userInfo: somethingToPass, repeats: false)
func tester(timer: NSTimer)
{
let theStringToPrint = timer.userInfo as String
println(theStringToPrint)
}
Include the colon in the selector text (tester:), and your parameter(s) go in userInfo.
Your function should take NSTimer as a parameter. Then just extract userInfo to get the parameter that passed.
Selectors are an internal representation of a method name in Objective-C. In Objective-C "#selector(methodName)" would convert a source-code method into a data type of SEL. Since you can't use the #selector syntax in Swift (rickster is on point there), you have to manually specify the method name as a String object directly, or by passing a String object to the Selector type. Here is an example:
var rightBarButton = UIBarButtonItem(
title: "Logout",
style: UIBarButtonItemStyle.Plain,
target: self,
action:"logout"
)
or
var rightBarButton = UIBarButtonItem(
title: "Logout",
style: UIBarButtonItemStyle.Plain,
target: self,
action:Selector("logout")
)
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
}
Objective-C Selector
Selector identifies a method.
//Compile time
SEL selector = #selector(foo);
//Runtime
SEL selector = NSSelectorFromString(#"foo");
For example
[object sayHello:#"Hello World"];
//sayHello: is a selector
selector is a word from Objective-C world and you are able to use it from Swift to have a possibility to call Objective-C from Swift It allows you to execute some code at runtime
Before Swift 2.2 the syntax is:
Selector("foo:")
Since a function name is passed into Selector as a String parameter("foo") it is not possible to check a name in compile time. As a result you can get a runtime error:
unrecognized selector sent to instance
After Swift 2.2+ the syntax is:
#selector(foo(_:))
Xcode's autocomplete help you to call a right method
Create Refresh control using Selector method.
var refreshCntrl : UIRefreshControl!
refreshCntrl = UIRefreshControl()
refreshCntrl.tintColor = UIColor.whiteColor()
refreshCntrl.attributedTitle = NSAttributedString(string: "Please Wait...")
refreshCntrl.addTarget(self, action:"refreshControlValueChanged", forControlEvents: UIControlEvents.ValueChanged)
atableView.addSubview(refreshCntrl)
//Refresh Control Method
func refreshControlValueChanged(){
atableView.reloadData()
refreshCntrl.endRefreshing()
}
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")
}
}
}
When using performSelector()
/addtarget()/NStimer.scheduledTimerWithInterval() methods your method (matching the selector) should be marked as
#objc
For Swift 2.0:
{
//...
self.performSelector(“performMethod”, withObject: nil , afterDelay: 0.5)
//...
//...
btnHome.addTarget(self, action: “buttonPressed:", forControlEvents: UIControlEvents.TouchUpInside)
//...
//...
NSTimer.scheduledTimerWithTimeInterval(0.5, target: self, selector : “timerMethod”, userInfo: nil, repeats: false)
//...
}
#objc private func performMethod() {
…
}
#objc private func buttonPressed(sender:UIButton){
….
}
#objc private func timerMethod () {
….
}
For Swift 2.2,
you need to write '#selector()' instead of string and selector name so the possibilities of spelling error and crash due to that will not be there anymore. Below is example
self.performSelector(#selector(MyClass.performMethod), withObject: nil , afterDelay: 0.5)
It may be useful to note where you setup the control that triggers the action matters.
For example, I have found that when setting up a UIBarButtonItem, I had to create the button within viewDidLoad or else I would get an unrecognized selector exception.
override func viewDidLoad() {
super.viewDidLoad()
// add button
let addButton = UIBarButtonItem(image: UIImage(named: "746-plus-circle.png"), style: UIBarButtonItemStyle.Plain, target: self, action: Selector("addAction:"))
self.navigationItem.rightBarButtonItem = addButton
}
func addAction(send: AnyObject?) {
NSLog("addAction")
}
you create the Selector like below.
1.
UIBarButtonItem(
title: "Some Title",
style: UIBarButtonItemStyle.Done,
target: self,
action: "flatButtonPressed"
)
2.
flatButton.addTarget(self, action: "flatButtonPressed:", forControlEvents: UIControlEvents.TouchUpInside)
Take note that the #selector syntax is gone and replaced with a simple String naming the method to call. There’s one area where we can all agree the verbosity got in the way. Of course, if we declared that there is a target method called flatButtonPressed: we better write one:
func flatButtonPressed(sender: AnyObject) {
NSLog("flatButtonPressed")
}
set the timer:
var timer = NSTimer.scheduledTimerWithTimeInterval(1.0,
target: self,
selector: Selector("flatButtonPressed"),
userInfo: userInfo,
repeats: true)
let mainLoop = NSRunLoop.mainRunLoop() //1
mainLoop.addTimer(timer, forMode: NSDefaultRunLoopMode) //2 this two line is optinal
In order to be complete, here’s the flatButtonPressed
func flatButtonPressed(timer: NSTimer) {
}
I found many of these answers to be helpful but it wasn't clear how to do this with something that wasn't a button. I was adding a gesture recognizer to a UILabel in swift and struggled so here's what I found worked for me after reading everything above:
let tapRecognizer = UITapGestureRecognizer(
target: self,
action: "labelTapped:")
Where the "Selector" was declared as:
func labelTapped(sender: UILabel) { }
Note that it is public and that I am not using the Selector() syntax but it is possible to do this as well.
let tapRecognizer = UITapGestureRecognizer(
target: self,
action: Selector("labelTapped:"))
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!")
}
As many have stated selectors are an objective c way of dynamically calling methods that has been carried over to Swift, it some case we are still stuck with it, like UIKit, probable because they where working on SwiftUI to replace it but some api have more swift like version like Swift Timer, for example you can use
class func scheduledTimer(withTimeInterval interval: TimeInterval,
repeats: Bool,
block: #escaping (Timer) -> Void) -> Timer
Instead, you can then call it like
Timer.scheduledTimer(withTimeInterval: 1,
repeats: true ) {
... your test code here
}
or
Timer.scheduledTimer(withTimeInterval: 1,
repeats: true,
block: test)
where the method test takes a Timer argument, or if you want test to take an named argument
Timer.scheduledTimer(withTimeInterval: 1,
repeats: true,
block: test(timer:))
you should also be using Timer not NSTimer as NSTimer is the old objective-c name
Change as a simple string naming in the method calling for selector syntax
var timer1 : NSTimer? = nil
timer1= NSTimer(timeInterval: 0.1, target: self, selector: Selector("test"), userInfo: nil, repeats: true)
After that, type func test().
For Swift 3
//Sample code to create timer
Timer.scheduledTimer(timeInterval: 1, target: self, selector: (#selector(updateTimer)), userInfo: nil, repeats: true)
WHERE
timeInterval:- Interval in which timer should fire like 1s, 10s, 100s etc. [Its value is in secs]
target:- function which pointed to class. So here I am pointing to current class.
selector:- function that will execute when timer fires.
func updateTimer(){
//Implemetation
}
repeats:- true/false specifies that timer should call again n again.
Selector in Swift 4:
button.addTarget(self, action: #selector(buttonTapped(sender:)), for: UIControlEvents.touchUpInside)
For swift 3
let timer = Timer.scheduledTimer(timeInterval: 0.01, target: self, selector: #selector(self.test), userInfo: nil, repeats: true)
Function declaration In same class:
#objc func test()
{
// my function
}

Resources