iOS delegate入門

今回はiOS開発において重要なdelegateについて説明します。

環境

  • Xcode 10.1

delegateとは

delegateとは何か

delegateとはあるクラスの処理をdelegateが橋渡しとなり違うクラスに処理を任せることです。

もしJavaをやっている方であればinterfaceを想像してもらえればそれと同様の意味です。

メモ
ちなみにdelegateという英単語は「委譲する・委任する」という意味なので、この意味で考えてもらっても想像しやすいかもしれません。

現時点でわからなくてもこのあと例と一緒に詳しく説明します!

delegateのサンプルコード

理解を深めていくために、実際にサンプルコードを書いてみましょう。

今回は、ボタンを押すと1秒後にアラートが表示されるサンプルアプリで説明していきます。

完成イメージはこちらです。

goal_image

全体のイメージ

今回はボタンのタップイベントを拾いアラートを表示する ViewController と時間を管理する TimeManager 、時間が経過したことを知らせる TimeManagerDelegate を用意します。

TimeManagerDelegateViewController が実装し時間が経過した通知を受け取れるようにします。

それぞれの関係は下の図になります。

class_relation
  1. ViewControllertappedButton() でタップイベントを拾い TimeManagerwaitOneSecond() を呼び出す(TimeManager では時間を計測し始める)
  2. 1秒が経過されると TimeManager ではTimeManagerDelegatetimeIsUp() を呼び出す

こうすることでdelegateを通じて ViewController に処理を渡すことができます。

delegateを実装

それでは上の説明をコードで実装をしていきます。

まずは全体のコードを見てみましょう。

import UIKit

class ViewController: UIViewController, TimeManagerDelegate {

    let timeManager = TimeManager()

    override func viewDidLoad() {
        super.viewDidLoad()
        // delegateをselfに指定
        timeManager.delegate = self
    }

    // ボタンのタップイベント
    @IBAction func tappedButton(_ sender: Any) {
        // ① TimeManagerのwaitOneSecond()を呼び出す
        timeManager.waitOneSecond()
    }

    // TimeManagerDelegateの関数
    func timeIsUp(message: String) {
        // ③ TimeManagerが呼び出す
        let alert = UIAlertController(title: nil, message: message, preferredStyle: .alert)
        let action = UIAlertAction(title: "OK", style: .default, handler: nil)
        alert.addAction(action)
        present(alert, animated: true)
    }
}

class TimeManager {
    // delegateを呼び出せるようにプロパティで持っておく
    weak var delegate: TimeManagerDelegate? = nil

    func waitOneSecond() {
        Timer.scheduledTimer(withTimeInterval: 1, repeats: false) { _ in
            // ② 1秒が経過したらTimeManagerDelegateのtimeIsUp()を呼び出す
            self.delegate?.timeIsUp(message: "1秒たちました")
        }
    }
}

protocol TimeManagerDelegate: NSObjectProtocol {
    func timeIsUp(message: String)
}

それでは1つ1つ見ていきましょう。

class ViewController: UIViewController, TimeManagerDelegate {

    let timeManager = TimeManager()

    override func viewDidLoad() {
        super.viewDidLoad()
        // delegateをselfに指定
        timeManager.delegate = self
    }
}

上記の1行目で TimeManagerDelegate を実装しています。

次に viewDidLoad()timeManagerdelegate にselfを指定することでdelegate関数が呼ばれるようになります。

@IBAction func tappedOneButton(_ sender: Any) {
    // ① TimeManagerのwaitOneSecond()を呼び出す
    timeManager.waitOneSecond()
}

次は先ほどのイメージ画像の①に該当するボタンのタップイベントです。
ここでは timeManagerwaitOneSecond() 関数を呼び出しているだけですね。

func waitOneSecond() {
    Timer.scheduledTimer(withTimeInterval: 1, repeats: false) { _ in
        // ② 1秒が経過したらTimeManagerDelegateのtimeIsUp()を呼び出す
        self.delegate?.timeIsUp(message: "1秒たちました")
    }
}

イメージ画像の順番通りに説明するためソースコードが少し飛びますが、
イメージ画像の②に該当する TimeManagerwaitOneSecond() 関数です。

ここでは Timer クラスを使用して1秒が経過した後に delegatetimeIsUp() を呼んでいます。

// TimeManagerDelegateの関数
func timeIsUp(message: String) {
    // ③ TimeManagerが呼び出す
    let alert = UIAlertController(title: nil, message: message, preferredStyle: .alert)
    let action = UIAlertAction(title: "OK", style: .default, handler: nil)
    alert.addAction(action)
    present(alert, animated: true)
}

次は ViewController で実装している TimeManagerDelegatetimeIsUp() 関数です。
イメージ画像では③に該当します。

ここは先ほど見たように TimeManager が1秒経過後に呼び出しており、
ViewController に処理を任せています。

それではここでビルドしてみましょう。
このような動きになっているば成功です!

goal_image

最後に

今回はサンプルを用いることでdelegateを使った処理の譲渡を見ることができたかと思います。

ただdelegateの理解はiOS開発の中でも大きなハードルであると考えています。
このような概念を理解することは自分の中でのイメージが重要ですので、
実装する際には処理を譲渡する役( TimeManager )、処理を譲渡される役( ViewController )、
処理を伝える役( TimeManagerDelegate )の3つの役者を整理することが大切です。