デリゲートとは
delegateは英語で「人に任せる」という意味を持っており、
「あるクラスから他のクラスに処理を任せる」というデザインパターンです。
デリゲートを使う意味
https://qiita.com/s_higeru/items/d156d2dbc725b88c220e ←こちらを参照
要約すると、
「呼び出し先のclassが、呼び出し元のclassに、処理を依頼したくなったから」
です。
delegate(やclosure)を使わないと循環参照してしまいます....
デリゲートの実装方法
delegateを実装するには、3つの役割が必要になります。
-
プロトコル
任せる処理をメソッドとして定義します。このメソッドはデリゲートメソッドと呼ばれます。 -
デリゲートを持つクラス
デリゲートメソッドを使用し、処理の流れを記述します。 -
デリゲートを実装するクラス
プロトコルで定義されたデリゲートメソッドを実装します。
デリゲートを使用するメリット
一般的にデリゲートを使うメリットとして
- コードの分離
- 拡張性
- 再利用性
の3つが挙げられる。
メリットを体感するために簡単なコードを書いて比較する。
(コードの分離は今回省略)
拡張性
デリゲートとして使用するプロトコル(Protocol)を定義
// ボタンがタップされたときに呼ばれるメソッドを持つプロトコル
protocol ButtonDelegate: AnyObject {
func didTapButton()
}
次に、デリゲートを持つボタンのクラスを作成します。
import UIKit
class DelegateButton: UIButton {
// デリゲートオブジェクトを保持するプロパティ
weak var delegate: ButtonDelegate?
// ボタンがタップされたときの処理
@objc private func buttonTapped() {
// デリゲートのメソッドを呼び出す
delegate?.didTapButton()
}
// 初期化処理
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
// ボタンがタップされたときのイベントを登録
self.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
}
}
実際にButtonDelegateプロトコルを実装するクラスを定義してみましょう。
class ViewController: UIViewController, ButtonDelegate {
override func viewDidLoad() {
super.viewDidLoad()
let button = DelegateButton(type: .system)
button.setTitle("タップしてください", for: .normal)
button.frame = CGRect(x: 100, y: 200, width: 200, height: 50)
button.delegate = self // ボタンのデリゲートをViewControllerに設定
self.view.addSubview(button)
}
// ButtonDelegateプロトコルのメソッドを実装
func didTapButton() {
print("ボタンがタップされました!")
}
}
ここから拡張させます。
次に、デリゲートを準拠するクラスを定義し、そのクラスのインスタンスをDelegateButtonのデリゲートとして設定します。
// ボタンのタップイベントを処理するクラス
class TapHandler: ButtonDelegate {
func didTapButton() {
print("ボタンがタップされました!")
// 他の処理を追加できます
}
}
ViewControllerクラス内でDelegateButtonとTapHandlerクラスを組み合わせます。
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let button = DelegateButton(type: .system)
button.setTitle("タップしてください", for: .normal)
button.frame = CGRect(x: 100, y: 200, width: 200, height: 50)
let tapHandler = TapHandler()
button.delegate = tapHandler // ボタンのデリゲートをTapHandlerに設定
self.view.addSubview(button)
}
}
このコードでは、DelegateButtonのインスタンスを作成し、それに対してTapHandler
のインスタンスをデリゲートとして設定しています。ボタンがタップされると、DelegateButton内のbuttonTapped()
メソッドが呼び出され、その中でデリゲートのdidTapButton()
メソッドが実行されます。そして、TapHandler
クラス内のdidTapButton()
メソッドが呼び出され、"ボタンがタップされました!"というメッセージがコンソールに表示されます。
このような構成により、ViewControllerは特定の処理を行うことなく、ボタンのタップイベントをTapHandler
クラスに委譲することができます。そして、必要に応じて新しいクラスを作成してデリゲートとして指定することで、ボタンのタップイベントを異なる振る舞いで処理できるようになります。このようにデリゲートを使用することで、ボタンの振る舞いを柔軟に拡張できるというのが拡張性の利点です。
再利用性
デリゲートとして使用するプロトコルおよびデリゲートを持つカスタムボタンのクラスは拡張性と同じものを使用する。
このカスタムボタンを使用して複数の画面で異なる処理を行う例としてまず、DelegateButtonをデリゲートとするViewControllerを作成する。
class ViewController1: UIViewController, ButtonDelegate {
override func viewDidLoad() {
super.viewDidLoad()
let button = DelegateButton(type: .system)
button.setTitle("画面1でタップ", for: .normal)
button.frame = CGRect(x: 100, y: 200, width: 200, height: 50)
button.delegate = self // ボタンのデリゲートをViewController1に設定
self.view.addSubview(button)
}
// ButtonDelegateプロトコルのメソッドを実装
func didTapButton() {
print("画面1: ボタンがタップされました!")
}
}
ここではViewController1
クラスがButtonDelegate
プロトコルを準拠し、didTapButton()
メソッドを実装しています。このクラスではボタンがタップされたときに「画面1: ボタンがタップされました!」と表示します。
同様に、別のViewControllerを作成し、DelegateButtonを使って異なる処理を実装します。
class ViewController2: UIViewController, ButtonDelegate {
override func viewDidLoad() {
super.viewDidLoad()
let button = DelegateButton(type: .system)
button.setTitle("画面2でタップ", for: .normal)
button.frame = CGRect(x: 100, y: 200, width: 200, height: 50)
button.delegate = self // ボタンのデリゲートをViewController2に設定
self.view.addSubview(button)
}
// ButtonDelegateプロトコルのメソッドを実装
func didTapButton() {
print("画面2: ボタンがタップされました!")
}
}
このように、ViewController1
とViewController2
で同じDelegateButton
を使いながら、ボタンがタップされたときに表示されるメッセージを別々に定義することができます。それぞれのViewControllerが異なる処理を持ちながら、同じカスタムボタンを再利用しているのが再利用性の利点です。
このようにデリゲートを使用することで、カスタムボタンを汎用的に作成し、異なる画面でそれぞれの処理を実装できます。その結果、同じカスタムボタンを複数の場所で再利用することができ、コードの重複を減らすことができます。再利用性を高めることにより、アプリケーションの保守性や拡張性を向上させることができます。