Can an NSTimer take multiple selectors? - ios

I'm trying to use an NSTimer in my app, and was wondering if it's possible to call two methods when the timer fires.
Here's the code:
gameTimer = NSTimer.scheduledTimerWithTimeInterval(0.01, target: self, selector:
Selector("gameMovement" && "fireBullet"), userInfo: nil, repeats: true)
I'm getting an error saying there are two arguments in the Selector.

Nope. You would call just one method that delegates to all the things you want.
func someFunc() {
gameTimer = NSTimer.scheduledTimerWithTimeInterval(
0.01,
target: self,
selector: Selector("timerFired"),
userInfo: nil,
repeats: true
)
}
func timerFired() {
gameMovement()
fireBullet()
}
This is a more maintainable pattern anyway, as it's easier to see how your code flows.

Related

Scheduled timers in SpriteKit

I have scheduled timers that add sprite nodes to the screen as obstacles
func timers(){
personTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(spawnPerson), userInfo: nil, repeats: true)
bikeTimer = Timer.scheduledTimer(timeInterval: 2, target: self, selector: #selector(spawnBike), userInfo: nil, repeats: true)
motorcycleTimer = Timer.scheduledTimer(timeInterval: 2.5, target: self, selector: #selector(spawnMotorcycle), userInfo: nil, repeats: true)
}
I added a function to invalidate those timers. so that a bonus "level" can be ran.
func invalidateTimers(){
// Obstacles
personTimer.invalidate()
bikeTimer.invalidate()
motorcycleTimer.invalidate()
}
When the bonus is called
func bonus() {
invalidateTimers()
bonusTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(spawnDiamonds), userInfo: nil, repeats: true)
}
The problem that I'm having is that when the bonus is done running I invalidate the bonusTimer and recall timers(). But when I do all the timers in the function seem to be firing twice. Whats an easy workaround for that since they can't just be paused.
Instead of using timers, consider using SKActions, as they work well with SpriteKit. To start the timer, run:
let wait1 = SKAction.wait(forDuration: 1)
let personTimer = SKAction.repeatForever(SKAction.sequence([wait1, SKAction.run {
spawnPerson() // spawnBike() etc. for each different timer
}]))
self.run(personTimer, withKey: "spawnPerson")
with modified wait values and function calls for each different timer. Then to stop the timer, run:
self.removeAction(forKey: "spawnPerson")
for each action using a different key.
Instead of using timers, you can use update: method of your SKScene subclass. It calls once per frame and has currentTime parameter. So you can easily calculate time intervals you need and trigger corresponding methods.

Swift NSTimer unrecognized selector sent to instance timerFireMethod

I'm writing some timer code in Swift for iOS 9.2
I have the latest iOS 9.2 docs downloaded through xcode
They show
(void)timerFireMethod:(NSTimer *)timer
But this will not work.
If I use signatures like these
func timerFire(timer : NSTimer?)
func timerFire(timer : NSTimer)
Then I get the error
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[DSP1.PlayManager timerFire]: unrecognized selector sent to instance
The only thing I can get to work is a call signature like this
func play(sound : String)
{
bsound = theLM?.getPlayer(sound)
bsound?.delegate = self
bsound?.play()
stimer = NSTimer(timeInterval: 1.0, target: self, selector: Selector("timerFire"), userInfo: self, repeats: true)
NSRunLoop.currentRunLoop().addTimer(stimer!, forMode: NSDefaultRunLoopMode)
}
// Docs say signature should be (void)timerFireMethod:(NSTimer *)timer. Docs are wrong
func timerFire()
{
print("Player at: \(bsound?.currentTime) out of \(bsound?.duration) seconds");
}
But this does not match what the latest iOS 9.2 docs downloaded through XCode say should work.
Am I doing this right?
Why do the freshly loaded iOS 9.2 docs seem to have the wrong signature?
What are other people reading for accurate documentation for Swift iOS programming?
(Edited to be more clear that the callback signatures listed in the docs fail to work at runtime)
Answer:
In the call to NSTimer, if your function name passed in as Selector has a trailing colon, it means you want the timer passed as an argument to your method. No colon means you don't want the timer passed as an argument.
NSTimer(timeInterval: 1.0, target: self, selector: Selector("timerFire"), userInfo: self, repeats: true)
func timerFire()
OR
NSTimer(timeInterval: 1.0, target: self, selector: Selector("timerFire:"), userInfo: self, repeats: true)
func timerFire(timer : NSTimer)
The documentation for NSTimer mentions this for the selector argument, but is far from clear. "The selector should have the following signature: timerFireMethod: (including a colon to indicate that the method takes an argument). "
You've made a simple, yet common mistake.
Your method signature should be:
func timerFire(timer: NSTimer) {}
And your timer setup should be:
NSTimer(timeInterval: 1.0, target: self, selector: "timerFire:", userInfo: nil, repeats: true)
The mistake is that you're missing the colon in the selector name. timerFire is different from timerFire:. Skip the colon and it'll look for for a method like this:
func timerFire() {}
Without the NSTimer parameter. It's best though to include the parameter, and thus the colon, so that you can confirm the timer you get is the one you expect.
The same is true for notifications. If you're using Notification Center, include the colon, and the Notification object in the method.
Your code seems to be ok. The selector for your timer does not have a predefined signature, you can all it whatever you like as long a you have a method in your class with that name.
func play(sound : String) {
// ....
stimer = NSTimer(timeInterval: 1.0, target: self, selector: "methodToRunOnTimerTick", userInfo: self, repeats: true)
NSRunLoop.currentRunLoop().addTimer(stimer!, forMode: NSDefaultRunLoopMode)
}
func methodToRunOnTimerTick() {
print("Player at: \(bsound?.currentTime) out of \(bsound?.duration) seconds");
}
One more thing to remember is that the method you decide to use can also receive the timer as a parameter when it is being called. This case would look like this:
func play(sound : String) {
// ....
stimer = NSTimer(timeInterval: 1.0, target: self, selector: "methodToRunOnTimerTick:", userInfo: self, repeats: true)
NSRunLoop.currentRunLoop().addTimer(stimer!, forMode: NSDefaultRunLoopMode)
}
func methodToRunOnTimerTick(timer: NSTimer) {
print("Player at: \(bsound?.currentTime) out of \(bsound?.duration) seconds");
}
So you implement the timer the right way, the method does not need a special signature. Let me know if you need more help. Good luck with your project!

How to add a timer to a function - IOS Swift

Say I had a function, any function, that I wanted to run for only three seconds and then never run again. How would I do this? Would I use NSTimer? Thank you for your help.
Here you go,
NSTimer.scheduledTimerWithTimeInterval(3, target: self, selector: Selector("updateProgress:"), userInfo: nil, repeats: false)
func updateProgress(timer: NSTimer){
}

Define a variable as AnyObject so that it can be changed later to NSTimer--Swift

So, I am trying to make a timer (run correctly) in swift.
func doSomething(){
println("Did something")
}
#IBActionFunc createTimer: AnyObject{
var timer = NSTimer(timeInterval: 0.2, target: self, selector: "doSomething", userInfo: nil, repeats: true)
}
#IBActionFunc stopTimer: AnyObject{
timer.invalidate()
}
Other option:
var timer:AnyObject = AnyObject
func doSomething(){
println("Did something")
}
#IBActionFunc createTimer: AnyObject{
timer = NSTimer(timeInterval: 0.2, target: self, selector: "doSomething", userInfo: nil, repeats: true)
}
#IBActionFunc stopTimer: AnyObject{
timer.invalidate()
}
I am not sure if this should actually work. From my tests, it does not because "timer" is defined as a local variable (?), so it cannot be accessed from other functions (?). To try to fix this, I first defined "var timer" to AnyObject, so that it can be redefined as anything later. Sadly, I get crazy errors and no's. Should I even need to do this second thing, or should the first one work? Thanks in advance!
You're right in that you currently have a local variable, so you can't reference it in other functions. You need an instance variable, such as:
class MyClass {
var timer: NSTimer
...
#IBActionFunc createTimer: AnyObject{
timer = NSTimer(timeInterval: 0.2, target: self, selector: "doSomething", userInfo: nil, repeats: true)
}
...

Extra argument 'selector' in call error

class ViewController: UIViewController {
func ChangePage()
{
NSLog("Hej")
}
var timers = NSTimer(NSTimeInterval(0.5), target:self, selector: "ChangePage", userInfo: nil, repeats: true)
}
I get the following error from Xcode 6:
Extra Argument 'selector' in call
I've tried several configurations, does it have something to do with where in the code it's placed?
You might want to use:
var timer = NSTimer.scheduledTimerWithTimeInterval(0.5, target: self, selector: "changePage", userInfo: nil, repeats: true)
This returns a timer that is already added to the run loop and fires automatically.
To stop the timer to fire, you must invalidate it like this
timer.invalidate()
You should add timeInterval in the constructor like:
NSTimer(timeInterval: NSTimeInterval(0.5), target:self, selector: "ChangePage", userInfo: nil, repeats: true)
And yes, it does matter where you put. The problem is, that timers is a property, and it is created before the initialization. So when it is created, self is not existing, but you refer to it, and that causes the problem.

Resources