MILLEN BOX

音楽好きの組み込みソフトエンジニアによるプログラミング(主にiOSアプリ開発)の勉強の記録

swiftでタイマー処理がしたい [swift1.2]

昨日投稿したXCTestの記事が割とアクセスされていてびっくりしています。こんにちは。

〜Tests.swiftって何?XCTestでの単体テストのススメ [swift1.2] - MILLEN BOX

今日はタイマー処理をテーマにしたいと思います。
高橋名人もびっくりの連打アプリを作成しました。10秒間の間に何回ボタンをタップできるかを競います。
尚、本アプリはテストアプリですので、もう一度プレイしたければ再度ビルド実行お願いします!

f:id:anthrgrnwrld:20150806213220g:plain

テストアプリのGithubは以下です。

github.com

10秒という時間を計るにはswiftではどうすればいいのでしょう?
その仕組みは以下のようになります。

自分ポイント1

残念ながら10秒たったら状態を変更するような機能はswiftには存在しません。
しかし「決められた時間毎に教えてくれる」機能は持っています。
決められた時間は今回の場合は1秒です。(減っていく時間を表示したいから)
まず1秒毎に実行したい関数を作成します。
以下の updateTimer() が1秒毎に実行されれば、関数内でcountupTimerがインクリメントされることによってタイマー開始から現在までの秒数がわかります。

import UIKit

class ViewController: UIViewController {

    ...
    
    /**
    タイマー関数。1秒毎に呼び出される。
    */
    func updateTimer() {
        println("\(__FUNCTION__) is called")

        countupTimer++                                      //countupTimerをインクリメント
        let countdownTimer = editTimerCount(countupTimer)   //カウントアップ表記をカウントダウン表記へ変換
        updateTimerLabel(countdownTimer)                    //タイマー表示ラベルをアップデート
        
        if countdownTimer <= 0 {
            timerState = false
            startState = false
            countupTimer = 0
            timer.invalidate()
        }
        
        println("\(countupTimer)")
    }

}

自分ポイント2

1秒毎に updateTimer() を実行するにはどのようにすればいいでしょうか。 NStimerクラスの変数(正確にはインスタンス)を作成し、そいつにNSTimer.scheduledTimerWithTimeInterval を放り込むと決められた時間毎にお知らせしてくれるタイマーがスタートします。
NSTimer.scheduledTimerWithTimeInterval 呼び出し時の引数によってタイマーの時間、その際に呼び出される関数が変更されます。
今回は (1, target: self, selector: Selector("updateTimer"), userInfo: nil, repeats: true)となります。 第1引数がタイマーの秒数、第3引数 selectorが呼び出される関数です。

自分ポイント3

10秒たったらタイマーの実行を中止したいです。
そのような時には中止したい箇所で timer.invalidate() を実行しましょう。

class ViewController: UIViewController {
    
    @IBOutlet var counterDigit: [UILabel]!
    @IBOutlet var decimalPlace: [UILabel]!
    
    var countPushing = 0
    var countupTimer = 0
    var timer = NSTimer()      //NSTimerのインスタンス作成
    var timerState = false
    var startState = true
    
    
    
    ....

    @IBAction func buttonA(sender: AnyObject) {
        
        pressButtonFunc()
        
    }

    @IBAction func buttonB(sender: AnyObject) {

        pressButtonFunc()
        
    }

    
    /**
    ボタンA及びBを押した時に実行する関数
    */
    func pressButtonFunc() {

        //println("\(__FUNCTION__) is called")

        //timerStateがfalseの時にはTimerをスタート。trueの時には無視する。
        if timerState == false && startState {
            timerState = true
            timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("updateTimer"), userInfo: nil, repeats: true)   //タイマーを作成!!!
        }
        
        if timerState && startState {
            
            countPushing++  //countPushingをインクリメント
            
            let countAfterEdit = editCount(countPushing, digitNum: counterDigit.count)  //カウントを表示用にEdit
            updateCounter(countAfterEdit)   //カウンタをアップデートする
            
        }

        
    }

    /**
    タイマー関数。1秒毎に呼び出される。
    */
    func updateTimer() {
        println("\(__FUNCTION__) is called")

        countupTimer++                                      //countupTimerをインクリメント
        let countdownTimer = editTimerCount(countupTimer)   //カウントアップ表記をカウントダウン表記へ変換
        updateTimerLabel(countdownTimer)                    //タイマー表示ラベルをアップデート
        
        if countdownTimer <= 0 {
            timerState = false
            startState = false
            countupTimer = 0
            timer.invalidate()
        }
        
        println("\(countupTimer)")
    }  

}

アプリとしては色々課題がありますが、タイマー処理を持ったアプリが完成しました。
時間制限を持ったゲームなどを作成する場合には必須なのではないでしょうか。