SwiftのTimerクラスを初心者でも理解できる10選の使用例

SwiftのTimerクラス使用例イメージ Swift
この記事は約18分で読めます。

【サイト内のコードはご自由に個人利用・商用利用いただけます】

この記事では、プログラムの基礎知識を前提に話を進めています。

説明のためのコードや、サンプルコードもありますので、もちろん初心者でも理解できるように表現してあります。

基本的な知識があればサンプルコードを活用して機能追加、目的を達成できるように作ってあります。

※この記事は、一般的にプロフェッショナルの指標とされる『実務経験10,000時間以上』を凌駕する現役のプログラマチームによって監修されています。

サイト内のコードを共有する場合は、参照元として引用して下さいますと幸いです

※Japanシーモアは、常に解説内容のわかりやすさや記事の品質に注力しております。不具合、分かりにくい説明や不適切な表現、動かないコードなど気になることがございましたら、記事の品質向上の為にお問い合わせフォームにてご共有いただけますと幸いです。
(送信された情報は、プライバシーポリシーのもと、厳正に取扱い、処分させていただきます。)

はじめに

SwiftのTimerクラスの使い方について学ぼうとしているあなたへ。

あなたがこの記事を読むことで、Timerクラスの基本から高度な使用方法まで、10の具体的なサンプルコードを通じてしっかりと学べるようになります。

Timerクラスは非常に便利なクラスで、様々なタイミングでの処理を実行するのに役立ちますが、使い方を間違えると思わぬトラブルの原因にもなり得ます。そんなトラブルを避け、効果的にTimerクラスを利用するためのポイントを、初心者にもわかるように丁寧に解説していきます。

●SwiftのTimerクラスとは

Timerクラスは、SwiftのFoundationフレームワークに含まれるクラスの一つです。

このクラスを使用することで、特定の時間が経過した後に特定の処理を実行する、または一定の間隔で繰り返し特定の処理を実行するといった動作をプログラム内で実現することができます。

○Timerクラスの基本概念

Timerクラスは、指定した時間が経過すると指定したメソッドを呼び出す機能です。

例えば、5秒後に何かの処理をしたい、または毎秒何かの処理を繰り返したいといった場合に使用します。

Timerは、一度だけ実行するものと、繰り返し実行するものの2種類があります。

実際にTimerを使用する場合、次のようなステップで操作します。

  1. Timerのインスタンスを作成する
  2. 作成したTimerのインスタンスを、RunLoopに追加する
  3. 必要がなくなったら、Timerを無効にする

●Timerクラスの使い方

SwiftのTimerクラスは非常に使い勝手が良く、様々なタイミングでの処理を手軽に実装できます。

しかし、その使い方にはポイントがいくつか存在します。

ここでは、基本的なタイマーの設定からリピート設定を持つタイマーの作成まで、実際のコードを交えながら解説していきます。

○サンプルコード1:基本的なタイマーの設定

最も基本的なTimerの設定方法を見てみましょう。

下記のコードでは、2秒後にprint関数を実行して「Timerが発火しました!」と表示するタイマーを設定しています。

import Foundation

let timer = Timer.scheduledTimer(withTimeInterval: 2.0, repeats: false) { _ in
    print("Timerが発火しました!")
}
RunLoop.current.run(until: Date().addingTimeInterval(3))

このコードを実行すると、2秒後に「Timerが発火しました!」とコンソールに表示されます。

repeatsパラメータをfalseにすることで、このタイマーは一度だけ動作します。

○サンプルコード2:リピート設定を持つタイマーの作成

次に、一定の間隔で繰り返し実行されるタイマーの作成方法を学びましょう。

下記のコードは、1秒ごとに「1秒経過」と表示するタイマーを設定するものです。

import Foundation

let repeatingTimer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { timer in
    print("1秒経過")
}
RunLoop.current.run(until: Date().addingTimeInterval(5))

このコードでは、repeatsパラメータをtrueに設定しているため、タイマーは繰り返し動作します。

具体的には、このコードを実行すると、5秒間で「1秒経過」というメッセージが5回コンソールに表示されます。

○サンプルコード3:タイマーの一時停止と再開

タイマーを一時的に停止して、後で再開する場合があります。

SwiftのTimerクラスには直接的な「停止」や「再開」のメソッドは存在しませんが、タイマーを無効化して新しいタイマーを作成することで、このような動作を模倣できます。

下記のコードは、3秒ごとに「タイマー作動中」と表示するタイマーを設定して、10秒後にタイマーを無効化し、その後5秒後に再開する例です。

import Foundation

var timer: Timer?
func startTimer() {
    timer = Timer.scheduledTimer(withTimeInterval: 3.0, repeats: true) { _ in
        print("タイマー作動中")
    }
}

startTimer()

// 10秒後にタイマーを一時停止
DispatchQueue.global().asyncAfter(deadline: .now() + 10) {
    timer?.invalidate()
    timer = nil
    print("タイマー停止")

    // さらに5秒後にタイマーを再開
    DispatchQueue.global().asyncAfter(deadline: .now() + 5) {
        print("タイマー再開")
        startTimer()
    }
}

RunLoop.current.run(until: Date().addingTimeInterval(30))

このコードを実行すると、最初は3秒おきに「タイマー作動中」と表示されます。

しかし、10秒後に「タイマー停止」と表示されてタイマーが一時的に停止します。

さらに5秒後に「タイマー再開」と表示され、タイマーが再び動作を開始します。

○サンプルコード4:タイマーの無効化

タイマーを完全に停止するには、invalidate()メソッドを使用します。

このメソッドは、タイマーを永続的に無効化するため、再開の際には新しいタイマーインスタンスを作成する必要があります。

ここでは、5秒後にタイマーを無効化するサンプルコードを紹介します。

import Foundation

let timer = Timer.scheduledTimer(withTimeInterval: 2.0, repeats: true) { _ in
    print("タイマー作動中")
}

// 5秒後にタイマーを無効化
DispatchQueue.global().asyncAfter(deadline: .now() + 5) {
    timer.invalidate()
    print("タイマー停止")
}

RunLoop.current.run(until: Date().addingTimeInterval(10))

このコードの結果、最初の5秒間は2秒おきに「タイマー作動中」と表示されますが、その後は「タイマー停止」と表示され、メッセージは表示されなくなります。

○サンプルコード5:特定の間隔で動作するタイマーの作成

特定の間隔でタイマーを実行させることも可能です。

この際には、timeIntervalパラメータに動作させたい間隔を秒単位で指定します。

下記のコードでは、2.5秒ごとに「特定の間隔でタイマー作動中」と表示するタイマーを設定しています。

import Foundation

let intervalTimer = Timer.scheduledTimer(withTimeInterval: 2.5, repeats: true) { _ in
    print("特定の間隔でタイマー作動中")
}

RunLoop.current.run(until: Date().addingTimeInterval(15))

このコードを実行すると、2.5秒おきに「特定の間隔でタイマー作動中」と表示されます。

●Timerクラスの応用例

SwiftのTimerクラスを用いることで、様々なシチュエーションに対応することができます。

一歩進んだタイマーの応用例をいくつか紹介します。

○サンプルコード6:カウントダウン機能の実装

タイマーを用いてカウントダウン機能を実装する際のサンプルコードを見てみましょう。

このコードでは、10秒からカウントダウンして0になった際に「終了」と表示します。

import Foundation

var count = 10

let countdownTimer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { timer in
    if count > 0 {
        print(count)
        count -= 1
    } else {
        print("終了")
        timer.invalidate()
    }
}

RunLoop.current.run(until: Date().addingTimeInterval(12))

このコードを実行すると、10から1まで順に数字が表示され、その後「終了」と表示されます。

○サンプルコード7:プログレスバーと連動したタイマーの制御

iOSアプリケーションの中で、タイマーをプログレスバーと連動させて進行状況を示すことはよくあります。

ここでは、Timerクラスを使用してプログレスバーの進行を制御する例を紹介します。

import UIKit

class ViewController: UIViewController {

    var timer: Timer?
    var progress: Float = 0.0

    let progressBar: UIProgressView = {
        let progress = UIProgressView(progressViewStyle: .default)
        progress.translatesAutoresizingMaskIntoConstraints = false
        return progress
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        setupProgressBar()
        startTimer()
    }

    func setupProgressBar() {
        view.addSubview(progressBar)
        // UIの位置やサイズの設定
        // 中心に配置
        progressBar.center = view.center
    }

    func startTimer() {
        timer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(updateProgress), userInfo: nil, repeats: true)
    }

    @objc func updateProgress() {
        if progress < 1.0 {
            progress += 0.01
            progressBar.setProgress(progress, animated: true)
        } else {
            timer?.invalidate()
        }
    }
}

このコードを実行すると、画面中央に配置されたプログレスバーが0.1秒ごとに徐々に進行していき、最終的には100%になります。

○サンプルコード8:ユーザー入力に基づくタイマーの設定

ユーザーからの入力に応じてタイマーを設定する方法は、カスタムタイマーの作成や特定の時間に通知を出すようなアプリケーションにおいて役立ちます。

ここでは、ユーザーが入力した秒数に基づいてタイマーを起動するサンプルコードを紹介します。

import Foundation

print("秒数を入力してください:")
if let input = readLine(), let interval = Double(input) {
    let timer = Timer.scheduledTimer(withTimeInterval: interval, repeats: false) { _ in
        print("指定された\(interval)秒が経過しました。")
    }
    RunLoop.current.run(until: Date().addingTimeInterval(interval + 1))
} else {
    print("正しい秒数を入力してください。")
}

このコードではユーザーからの秒数入力を待ち受け、入力された秒数が経過した後にメッセージを表示します。

○サンプルコード9:複数のタイマーを組み合わせた処理

複数のタイマーを組み合わせることで、複雑なタイミングでの処理を実現することができます。

ここでは、1秒ごとにカウントを進めるタイマーと、5秒後にそのカウントを停止する別のタイマーを組み合わせた例を紹介します。

import Foundation

var count = 0

let countUpTimer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { timer in
    count += 1
    print("経過時間:\(count)秒")
}

let stopTimer = Timer.scheduledTimer(withTimeInterval: 5.0, repeats: false) { _ in
    countUpTimer.invalidate()
    print("カウントを停止しました。")
}

RunLoop.current.run(until: Date().addingTimeInterval(7))

このコードを実行すると、1秒ごとにカウントが進行し、5秒後にカウントが停止するメッセージが表示されます。

○サンプルコード10:非同期処理との連携

非同期処理とタイマーを組み合わせることで、バックグラウンドでの処理とタイマーによる定期的なチェックなど、さまざまな応用が考えられます。

ここでは、非同期でデータをフェッチし、それが完了したらタイマーを利用して定期的にデータの更新チェックを行う例を紹介します。

import Foundation

func fetchData(completion: @escaping () -> Void) {
    DispatchQueue.global().async {
        // 仮のデータ取得処理
        sleep(2)
        completion()
    }
}

fetchData {
    let checkUpdateTimer = Timer.scheduledTimer(withTimeInterval: 10.0, repeats: true) { timer in
        print("データの更新をチェックします。")
    }
    RunLoop.current.run(until: Date().addingTimeInterval(35))
}

このコードでは、非同期処理でデータを取得した後、10秒ごとにデータの更新をチェックする処理が行われます。

●注意点と対処法

SwiftのTimerクラスを使用する際には、いくつかの注意点があります。

特に初心者の方がハマりやすい問題や、予期せぬ動作を引き起こす可能性がある点を挙げ、それらの対処法について解説します。

○タイマーのリーク問題とその対処法

Timerクラスは、特にクロージャ内で自身を参照する場合、メモリリークを引き起こす可能性があります。

これは、タイマーが自身を強参照してしまうため、タイマーが破棄されないという問題が生じます。

例えば、次のようなコードが考えられます。

class SampleClass {
    var timer: Timer?

    func startTimer() {
        timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in
            self.doSomething()
        }
    }

    func doSomething() {
        print("タイマー動作中")
    }
}

このコードではdoSomethingメソッドを呼び出すために、クロージャ内からselfを参照しています。

これにより、SampleClassのインスタンスが破棄されてもタイマーが破棄されず、メモリリークが発生します。

この問題の対処法として、クロージャ内でのselfの参照を弱参照にする方法があります。

class SampleClass {
    var timer: Timer?

    func startTimer() {
        timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak self] _ in
            self?.doSomething()
        }
    }

    func doSomething() {
        print("タイマー動作中")
    }
}

このように、[weak self]という記述をクロージャの前に追加することで、selfの参照を弱参照として扱うことができます。

これにより、SampleClassのインスタンスが破棄されると、タイマーも適切に破棄されるようになります。

○主要スレッドとバックグラウンドスレッドの違い

Timerクラスを使用する際には、どのスレッドでタイマーが動作するのかを理解することが重要です。

通常、タイマーはスケジュールされたスレッドで動作します。

このため、UIを更新するような処理をタイマー内で行う場合、主要スレッドでタイマーをスケジュールすることが必要です。

しかし、バックグラウンドスレッドでタイマーをスケジュールした場合、主要スレッドのUI更新処理が正しく動作しない可能性があります。

そのような場合、次のようにDispatchQueueを使用して主要スレッドでUI更新を行うようにコードを書く必要があります。

let timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in
    DispatchQueue.main.async {
        // ここでUIの更新処理を行う
    }
}

このコードのDispatchQueue.main.async部分は、ブロック内の処理を主要スレッドで非同期に実行するためのものです。

これにより、バックグラウンドスレッドでタイマーが動作していても、UIの更新は主要スレッドで行われます。

●カスタマイズ方法

SwiftのTimerクラスを用いてタイマーの機能をカスタマイズする方法について詳しく解説します。

ここでの目標は、タイマーのインターバルを動的に変更する技術や、特定の条件下でタイマーの動作を調整する方法を身につけることです。

○タイマーのカスタムインターバルの設定方法

Timerクラスを用いたプログラムにおいて、タイマーのインターバルを動的に変更する必要がある場合があります。

例として、ユーザーのアクションやアプリの状態に応じてタイマーのインターバルを変更するケースを考えます。

下記のサンプルコードは、タイマーのインターバルを動的に変更する一例です。

import Foundation

class DynamicIntervalTimer {
    var timer: Timer?
    var interval: TimeInterval

    // イニシャライザ
    init(interval: TimeInterval) {
        self.interval = interval
    }

    // タイマーの開始
    func start() {
        if timer == nil {
            timer = Timer.scheduledTimer(withTimeInterval: interval, repeats: true) { [weak self] _ in
                self?.executeTask()
            }
        }
    }

    // タイマーの停止
    func stop() {
        timer?.invalidate()
        timer = nil
    }

    // インターバルの変更
    func changeInterval(newInterval: TimeInterval) {
        stop()
        interval = newInterval
        start()
    }

    // タイマーのタスク
    func executeTask() {
        print("タスク実行中")
    }
}

このコードでは、DynamicIntervalTimerクラスにchangeIntervalメソッドを定義しています。

このメソッドは、タイマーを一度停止してインターバルを変更した後、再度タイマーを開始する役割を果たしています。

この処理により、動的にタイマーのインターバルを変更することが可能となります。

実行例すると、次のような結果となります。

let dynamicTimer = DynamicIntervalTimer(interval: 2.0)
dynamicTimer.start()

// ある条件や時間が経過した後にインターバルを変更
dynamicTimer.changeInterval(newInterval: 5.0)

タイマーが最初は2秒のインターバルで動作し、changeIntervalメソッドにより5秒のインターバルで動作するように変更されます。

この流れにおいて、「タスク実行中」という文字列がコンソールに定期的に出力されます。

まとめ

SwiftのTimerクラスは、様々なアプリケーションや機能の実現に役立つ非常に便利なクラスです。

この記事を通じて、Timerクラスの基本的な使い方から高度な応用例、注意点やカスタマイズ方法に至るまで、幅広くその魅力や機能を学ぶことができたかと思います。

特に、タイマーの動的なインターバルの変更や、複数のタイマーを組み合わせる方法、さらには非同期処理との連携など、実際のアプリケーション開発で頻繁に遭遇するシナリオを中心に解説しました。

これにより、初心者の方でもSwiftでのタイマーの実装に自信を持って取り組むことができるでしょう。

また、タイマーのリーク問題やスレッドに関する注意点も取り上げました。

これらの知識は、SwiftのTimerクラスを用いる際に避けられない重要なテーマとなります。

正確で効果的なタイマーの利用を実現するために、これらのポイントをしっかりと頭に入れておくことをおすすめします。

最後に、タイマーのカスタマイズ方法を紹介しました。

アプリケーションの要件やユーザーエクスペリエンスを最適化するために、これらのカスタマイズ手法を駆使してください。

SwiftのTimerクラスの使い方に迷っている初心者の方、そして既に経験のある開発者の方々にとっても、この記事がTimerクラスの理解を深め、更なるスキルアップの一助となることを心より願っています。

最後まで読んでいただき、ありがとうございました。