

なぞなぞアプリを作ろうと思い、カウントダウンの機能を加えたくてとりあえず調べて書いてみたのですがなぜか動いてくれません。Labelでカウントを行うことは可能でしょうか？  
また可能ならお手数ですがコードを教えてください。お願いします。

[code]

    import UIKit
    
    class ViewController: UIViewController {
    
        @IBOutlet weak var countLabel: UILabel!
    
    
    
        var timeCount = 100
        var myTimer = Timer()
    
        @objc func timerUpdate() {
            timeCount = timeCount - 1
            countLabel.text = String(timeCount)
            if timeCount == 0 {
                myTimer.invalidate()
            }
           func viewDidLoad() {
            super.viewDidLoad()
                myTimer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(ViewController.timerUpdate), userInfo: nil, repeats: true)
    
    
            }
        }
    
    }
    
[/code]


CC BY-SA 4.0
2018-08-07T10:23:50.033
47322
2018-08-07T11:23:08.947
2018-08-07T11:23:08.947
3060
29633
question
swift xcode
タイマーを使用したカウントダウンアプリが動作しない


コード全体を正しくインデントし直すとすぐわかるはずなのですが、あなたの`viewDidLoad()`は`timerUpdate()`メソッドの定義の中に完全に含まれてしまっています。

[code]

    @objc func timerUpdate() {
        //ここはtimerUpdate()メソッドの中
        //...
        func viewDidLoad() {
            super.viewDidLoad()
            myTimer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(ViewController.timerUpdate), userInfo: nil, repeats: true)
        }
        //ここもまだtimerUpdate()メソッドの中
    }
    
[/code]

Swiftは言語仕様として、このようなネストされた関数定義を許しているのですが、内側のネストされた関数定義はインスタンスメソッドとはみなされず、外側のメソッドの中だけで有効なローカル関数になります。

従ってあなたのコードには、本来の(インスタンスメソッドとしての)`viewDidLoad()`が存在しないと判断され、(ネストされた)内側の`viewDidLoad()`も呼ばれることはありません。

(この辺り、コンパイラ技術的には「使用されていないローカル関数がある」警告を出すのはそれほど難しくないはずなのですが…。)

とにかく、結果としてあなたの`myTimer`には正しくスケジューリングされた`Timer`は設定されておらず、(どんな挙動をするのかドキュメント化されていないイニシャライザ`init()`で作られた)「何の役にも立たない」`Timer`インスタンスが設定された状態になっています。当然のようにあなたがスケジュールしようとした動作は何も起こりません。

このような誤りが見つけにくくなっている原因としては、あなたのプロパティ`myTimer`の宣言が典型的なバッドプラクティスのコードになっていることが挙げられます。

[code]

    var myTimer = Timer()
    
[/code]

`Timer()`で得られるインスタンスがどのようなものか、はっきりあなたが理解していない限り、このようなプロパティ宣言は **絶対に**
するべきではありません。(残念ながらこのような非常に悪い書き方が、検索結果の上位に出てくることがしばしばあります。)

[code]

    var myTimer: Timer?
    
[/code]

のように宣言しておけば、初期化が終わっているはずの`myTimer`が`nil`のままであることに早めに気付けるでしょう。

そこら辺も含めて、あなたのコードを書き直すとこんな感じになります。

[code]

    import UIKit
    
    class ViewController: UIViewController {
    
        @IBOutlet weak var countLabel: UILabel!
    
        var timeCount = 100
        var myTimer: Timer? //### 変数を「何の役にも立たないインスタンス」で初期化するのは絶対に避けるべき
    
        @objc func timerUpdate() {
            timeCount = timeCount - 1
            countLabel.text = String(timeCount)
            if timeCount == 0 {
                myTimer?.invalidate()
                myTimer = nil //### invalidate()したTimerは再利用できないので、nilにしてしまう
            }
        }
    
        override func viewDidLoad() { //### 正しいviewDidLoad()ならoverrideが無いとエラーになる
            super.viewDidLoad()
            //### targetにselfを指定したら、#selectorの中身もself.で始める
            myTimer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(self.timerUpdate), userInfo: nil, repeats: true)
        }
    }
    
[/code]

そのほかにチェックすべき点、私の現在のオススメの書き方など`###`入りのコメントで示しています。内容をお確かめの上おためしください。


CC BY-SA 4.0
2018-08-07T11:07:33.057
47323
2018-08-07T11:07:33.057
13972
47322
answer