iOS開発において重要なdelegateについて解説します。
環境
- swift 5.7
- Xcode 14.0.1
- macOS 12.6 Monterey
delegateとは
delegateとはあるクラスの処理をdelegateが橋渡しとなり違うクラスに処理を渡すことです。
もしJavaをやっている方であればinterfaceを想像してもらえればそれと同様の意味です。
ちなみにdelegateという英単語は「委譲する・委任する」という意味なので、
この意味で考えてもらっても想像しやすいかもしれません。
現時点でわからなくてもこれから例と一緒に詳しく説明します!
サンプルコードでdelegateを理解
理解を深めていくために、実際にサンプルコードを書いてみましょう。
ボタンを押すと1秒後にアラートが表示されるサンプルアプリで説明していきます。
完成イメージはこちらです!
簡単なアプリで解説していきます。
全体のイメージ
今回は要素が3つあります。
- ViewController: ボタンのタップイベントを拾いアラートを表示
- TimeManager: 時間を管理
- TimeManagerDelegate: 時間が経過したことを知らせる
TimeManagerDelegateは ViewControllerが実装し、時間が経過した通知を受け取れるようにします。
それぞれの関係は下の図になります。
ViewController
のtappedButton()
でタップイベントを拾いTimeManager
のwaitOneSecond()
を呼び出す(TimeManager
では時間を計測し始める)- 1秒が経過されると
TimeManager
ではTimeManagerDelegate
のtimeIsUp()
を呼び出す
こうすることで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) { [weak self] _ 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
に準拠させています。
こうすることでViewController
はUIViewController
としても扱えるしTimeManagerDelegate
としても扱えるようになります。
次に viewDidLoad()
で timeManager
の delegate
にselfを指定することでdelegate関数が呼ばれるようにします。
これができるのはdelegate
の型がTimeManagerDelegate
であるためです
@IBAction func tappedOneButton(_ sender: Any) {
// ① TimeManagerのwaitOneSecond()を呼び出す
timeManager.waitOneSecond()
}
次は先ほどのイメージ画像の①に該当するボタンのタップイベントです。
ここでは timeManager
の waitOneSecond()
関数を呼び出しているだけですね。
説明は省略していますが今回はStoryBoardを使ってUIButtonを配置しています。
func waitOneSecond() {
Timer.scheduledTimer(withTimeInterval: 1, repeats: false) { [weak self] _ in
// ② 1秒が経過したらTimeManagerDelegateのtimeIsUp()を呼び出す
self?.delegate?.timeIsUp(message: "1秒たちました")
}
}
イメージ画像の順番通りに説明するためソースコードが少し飛びますが、
イメージ画像の②に該当する TimeManager
の waitOneSecond()
関数です。
ここでは Timer
クラスを使用して1秒が経過した後に delegate
の timeIsUp()
を呼んでいます。
// 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
で実装している TimeManagerDelegate
の timeIsUp()
関数です。
イメージ画像では③に該当します。
ここは先ほど見たように TimeManager
が1秒経過後に呼び出しており、ViewController
に処理を任せています。
それではここでビルドしてみましょう。
こんな動きになっていれば成功!
うまく処理が移譲できています。
まとめ
今回はサンプルを用いることでdelegateを使った処理の譲渡を見ることができたかと思います。
ただdelegateの理解はiOS開発の中でも大きなハードルであると考えています。
このような概念を理解することは自分の中でのイメージが重要ですので、
実装する際には処理を譲渡する役( TimeManager
)、処理を譲渡される役( ViewController
)、
処理の橋渡し役( TimeManagerDelegate
)の3つの役者を整理することが大切です。