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.
Related
I am trying to use the NSTimer to increment the progress bar in my app when recording voice (see the screenshot)
let timedClock = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("Counting:"), userInfo: nil, repeats: true)
internal func Counting(timer: NSTimer!) {
if timeCount == 0 {
//self.timedClock = nil
stopRecording(self) //performs segue to another view controller
} else {
timeCount--;
self.timer.text = "\(timeCount)"
}
print("counting called!")
progressBar.progress += 0.2
}
The progress bar works only for the first time after I compile and run the project. When the recording is finished, the app performs segue to another view controller to play the recorded audio. However, when I go back to the view for recording, the timer/progress bar automatically runs. I suspect the NSTimer object is still alive on the NSRunLoop. So I was wondering how to prevent the NSTimer from automatically running.
Inspired by the answer in this SO thread, I tried the following, but the NSTimer still automatically runs.
let timedClock = NSTimer(timeInterval: 1, target: self, selector: "Counting:", userInfo: nil, repeats: true)
NSRunLoop.currentRunLoop().addTimer(timedClock, forMode: NSRunLoopCommonModes)
This happens because when your controller created it's properties are automatically initialized. According to Apple Docs (and method's name) scheduledTimerWithTimeInterval create and return scheduled timer. So if you only want create your timer and call it by trigger function use it like this:
class MyClass {
var timer: NSTimer?
...
func enableTimer() {
timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("Counting:"), userInfo: nil, repeats: true)
}
func disableTimer() {
timer?.invalidate()
timer = nil
}
...
}
Sorry for the quick self-answer, as I just found out that I can use the invalidate() method to prevent the timer from automatically firing:
timedClock.invalidate()
Hope it helps someone in the future!
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.
I am using an NSTimer.scheduledTimerWithTimeInterval object but cannot increase the speed of the clock to faster than 1/1000. I have tried invalidating the clock and re-creating. timer2 & timer3 work fine on the iOS simulator but does not work when testing on iPhone 5s device. Speed stays always on 1/1000 even when firing timer2 or timer3. Below is my definition:
timer = NSTimer.scheduledTimerWithTimeInterval(0.001, target: self, selector: Selector("result"), userInfo: nil, repeats: true)
timer2 = NSTimer.scheduledTimerWithTimeInterval(0.002, target: self, selector: Selector("result"), userInfo: nil, repeats: true)
timer3 = NSTimer.scheduledTimerWithTimeInterval(0.003, target: self, selector: Selector("result"), userInfo: nil, repeats: true)
You cannot change a timer. If you want a timer running at a different interval, you have to invalidate and destroy the existing timer and replace it with a new one. That's quite a common thing to do; timers are lightweight objects.
I've made a flappy bird clone, but when trying to implement pause functionality, I've come across issues with my NSTimer, which is spawning the pipes.
var timer = NSTimer.scheduledTimerWithTimeInterval(moveSpeed, target: self, selector: Selector("makePipes"), userInfo: nil, repeats: true)
As NSTimers can't be paused, within the makePipes() function I've implemented a simple if-statement, checking whether the game is paused or not, and if it is, not spawning new pipes. However, the gap between each series of pipes is then inconsistent, due to the timer still firing during the paused state.
Also, when transitioning to the game state from the menu, the timer fires off-time, creating the first two pipes in quick succession.
Is there any alternative to the NSTimer to handle this functionality?
You can stop the timer calling the func invalidate() when your game is paused, and then restart it when your game is un-paused.
Update:
You can add a second timer that fires at the difference from the next fire and the time of pause, the second timer should fire the first timer and then reset the first timer to the initial time.
Steps:
Add lets say a timer that fires every 2 seconds
When game is paused, calculate the time interval from the timer.fireDate and timeOfPause, that should be intervalTillNextTrigger
Add a second afterPauseTimer that triggers at intervallTillNextTrigger and should not repeat
When the afterPauseTimer is called, trigger the first timer with timer.fire(), invalidate timer, because timer.fire() will not interrupt it's regular firing schedule, add timer again with 2 seconds firing interval and invalidate afterPauseTimer.
see code below:
//
// ViewController.swift
// swft ios
//
// Created by Marius Fanu on 30/12/14.
// Copyright (c) 2014 Marius Fanu. All rights reserved.
//
import UIKit
class ViewController: UIViewController {
var timer: NSTimer!
var isPaused = false
var isAfterPause = false
var intervalTillNextTrigger: NSTimeInterval = 0
var afterPauseTimer: NSTimer!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
timer = NSTimer(timeInterval: 2, target: self, selector: Selector("timerTriggerd"), userInfo: nil, repeats: true)
NSRunLoop.mainRunLoop().addTimer(timer, forMode: NSDefaultRunLoopMode)
timer.fire()
}
#IBAction func pauseButtonPressed(sender: UIButton) {
var now = NSDate()
println("now = \(now)")
if isPaused == true {
if isAfterPause {
isAfterPause = false
afterPauseTimer = NSTimer(timeInterval: intervalTillNextTrigger, target: self, selector: Selector("timerAfterIntervalTrigger"), userInfo: nil, repeats: false)
NSRunLoop.mainRunLoop().addTimer(afterPauseTimer, forMode: NSDefaultRunLoopMode)
}
timer = NSTimer(timeInterval: 2, target: self, selector: Selector("timerTriggerd"), userInfo: nil, repeats: true)
NSRunLoop.mainRunLoop().addTimer(timer, forMode: NSDefaultRunLoopMode)
}
else {
isAfterPause = true
intervalTillNextTrigger = timer.fireDate.timeIntervalSinceDate(now)
println("till next trigger \(intervalTillNextTrigger)")
timer.invalidate()
timer = nil
}
isPaused = !isPaused
}
func timerTriggerd() {
NSLog("Triggerd!")
}
func timerAfterIntervalTrigger() {
println("reset timer")
timer.fire()
timer.invalidate()
timer = nil
timer = NSTimer(timeInterval: 2, target: self, selector: Selector("timerTriggerd"), userInfo: nil, repeats: true)
NSRunLoop.mainRunLoop().addTimer(timer, forMode: NSDefaultRunLoopMode)
afterPauseTimer.invalidate()
afterPauseTimer = nil
}
}
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.