SpriteKit - Creating a timer

Solution 1:

In Sprite Kit do not use NSTimer, performSelector:afterDelay: or Grand Central Dispatch (GCD, ie any dispatch_... method) because these timing methods ignore a node's, scene's or the view's paused state. Moreover you do not know at which point in the game loop they are executed which can cause a variety of issues depending on what your code actually does.

The only two sanctioned ways to perform something time-based in Sprite Kit is to either use the SKScene update: method and using the passed-in currentTime parameter to keep track of time.

Or more commonly you would just use an action sequence that starts with a wait action:

id wait = [SKAction waitForDuration:2.5];
id run = [SKAction runBlock:^{
    // your code here ...
}];
[node runAction:[SKAction sequence:@[wait, run]]];

And to run the code repeatedly:

[node runAction:[SKAction repeatActionForever:[SKAction sequence:@[wait, run]]]];

Alternatively you can also use performSelector:onTarget: instead of runBlock: or perhaps use a customActionWithDuration:actionBlock: if you need to mimick the SKScene update: method and don't know how to forward it to the node or where forwarding would be inconvenient.

See SKAction reference for details.


UPDATE: Code examples using Swift

Swift 5

 run(SKAction.repeatForever(SKAction.sequence([
     SKAction.run( /*code block or a func name to call*/ ),
     SKAction.wait(forDuration: 2.5)
     ])))

Swift 3

let wait = SKAction.wait(forDuration:2.5)
let action = SKAction.run {
    // your code here ...
}
run(SKAction.sequence([wait,action]))

Swift 2

let wait = SKAction.waitForDuration(2.5)
let run = SKAction.runBlock {
    // your code here ...
}
runAction(SKAction.sequence([wait, run]))

And to run the code repeatedly:

runAction(SKAction.repeatActionForever(SKAction.sequence([wait, run])))

Solution 2:

In Swift usable:

var timescore = Int()  
var actionwait = SKAction.waitForDuration(0.5)
            var timesecond = Int()
            var actionrun = SKAction.runBlock({
                    timescore++
                    timesecond++
                    if timesecond == 60 {timesecond = 0}
                    scoreLabel.text = "Score Time: \(timescore/60):\(timesecond)"
                })
            scoreLabel.runAction(SKAction.repeatActionForever(SKAction.sequence([actionwait,actionrun])))

Solution 3:

I've taken the swift example above and added in leading zeros for the clock.

    func updateClock() {
    var leadingZero = ""
    var leadingZeroMin = ""
    var timeMin = Int()
    var actionwait = SKAction.waitForDuration(1.0)
    var timesecond = Int()
    var actionrun = SKAction.runBlock({
        timeMin++
        timesecond++
        if timesecond == 60 {timesecond = 0}
        if timeMin  / 60 <= 9 { leadingZeroMin = "0" } else { leadingZeroMin = "" }
        if timesecond <= 9 { leadingZero = "0" } else { leadingZero = "" }

        self.flyTimeText.text = "Flight Time [ \(leadingZeroMin)\(timeMin/60) : \(leadingZero)\(timesecond) ]"
    })
    self.flyTimeText.runAction(SKAction.repeatActionForever(SKAction.sequence([actionwait,actionrun])))
}