実はUITableViewとかで使われているアレです。過去の自分に優しく教える気持ちで書いていきます。
Delegateとはなんぞ?
そのまま書いちゃうと、処理の委譲です。
Aさんの処理してるんだけど、この処理ってBさんがやるべき(or というかBさんしかできないよね?)というときに使います。
正直、委譲とか言われてもピンとこないので、よく使われるUITableViewを例に説明していきます。
例1. UITableViewの場合
UITableViewはご存知の通り、TableViewの生成やイベントの処理を行ってくれます。しかし、どのような見た目なのか?データはどうするのか?イベントに対してどう処理をするのか?については、実装されていません。もしされていて、単一の挙動しかしなかったら、めちゃめちゃ使いにくいですよね?
「タップされたらどのような処理をするか?」は、委譲先に任せているのです。これがDelegate パターンというものです。
委譲先は誰?
UIViewControllerにUITableViewを実装する時に、よくこれを忘れて「行のタップが取れない…」と数時間ハマっていませんでしたか?
tableView.delegate = self
この処理は、「TableViewのDelegate(委譲先)は自分だ!」と宣言する処理です。
TableViewのDelegate が設定されていないと、いくら `tableView(_:didSelectRowAt:)` などのデリゲートメソッドを実装しても、TableViewは行選択イベントの委譲先を知らされていないので、どこに処理をお願いすれば良いのかわかりません。
ViewControllerが、TableViewのDelegateは自分だよ、というのを設定してあげることで初めて、TableViewのデリゲートメソッドの振る舞いをViewControllerが制御するできるようになるのです。
UITableViewはよく使われますが実装が見えにくいので、次の例で Delegate 元/先を実装しながらコードを見てみましょう。
例2. ViewControllerとPresenterの場合
登場人物は以下の2人です。
- ViewController → 画面を作ったりタップイベントなどを処理する人 (Delegate先)
- Presenter → タップイベントをどうするか決める人 (Delegate元)
ViewControllerはタップイベントを検知するけど、画面やデータを更新するかどうかはPresenterが決めてほしい!という状況です。
*軽くMVPというアーキテクチャに則るのですが、別な話になるので割愛します。2人の役割だけ押さえておいてください。
流れを整理すると
1. Viewはボタンタップなどのイベントを受ける
2. Presenterに通知する(presenter.increment())
3. Presenterはそれを受けてデータ更新などをし、delegate 経由で次の処理ViewControllerに指示する
のようになります。
実際のコードを見てみましょう。
// 委譲先はこのDelegateに準拠し、中身を実装する必要がある
// この例だと、ViewControllerがupdateCount()というメソッドがどういう処理をするか?を実装する
protocol CounterDelegate {
func updateCount(_ count: Int)
}
class Presenter {
private var count = 0
private var delegate: CounterDelegate?
func attachView(_ delegate: CounterDelegate) {
self.delegate = delegate
}
func incrementCount() {
count += 1
// Delegate先に 「updateCount()を実行して」 と指示をする
// この時、Delegate先はCounterDelegateに準拠していればなんでもよい(PresenterはDelegate先を知らなくて良い)
delegate?.updateCount(count)
}
}
class ViewController: UIViewController {
@IBOutlet private weak var countLabel: UILabel!
private let presenter = Presenter()
override func viewDidLoad() {
super.viewDidLoad()
// presenterのDelegateはViewControllerだよ!と教えてあげる
// これによって、updateCount()の処理がViewControllerに任されることになる。
presenter.attachView(self)
}
@IBAction func tapCountupButton(_ sender: Any) {
// タップイベントがきたので、一旦presenterに処理を任せる
presenter.incrementCount()
}
}
extension ViewController: CounterDelegate {
// Delegateに準拠し、メソッドを実行する
// Viewの更新はViewControllerがやるので、presenterはDelegate経由でupdateCount()を実行するよう指示する
func updateCount(_ count: Int) {
countLabel.text = count.description
}
}
このように、Delegateパターンを使うことで、PresenterはViewControllerが何者かはわからないが、CounterDelegateに準拠している(updateCount(count: Int) を実装している)ことがわかっているので、
ViewControllerと疎結合を保ったまま、ViewControllerのメソッドを叩くことができるのがメリットになります。
コメント
コメントを投稿