Swiftのデリゲートとプロトコルを自分で実装することについて理解したのでその書き方や仕組みをイラストで説明していく。
delegateとprotocolの理解と書き方
Abcというクラスを定義し、このAbcクラスの中でなにかのイベントが発生したときに、ViewControllerに通知する仕組みを作りたい。
このような事例で書き方を順を追って説明していく。
まず順番としてはAbcクラスとAbcDelegateプロトコルの枠を作る。
@obj protocol AbcDelegate { } class Abc { }
プロトコルを作るには先頭にアットマークをつけてobjとし、protoclに続いてプロトコル名を記述する。プロトコル名は対象の「クラス名」+「Delegate」とするのが慣例のようだ。
枠ができたら、Abcクラスの中にプロパティとしてdelegate変数をAbcDelegateクラスとして宣言する。この時点ではnilを入れておくのでOptional型で宣言しておく。delegate変数名は別にmyDelegateとかでも構わない。
@obj protocol AbcDelegate { } class Abc { weak var delegate = AbcDelegate? }
AbcDelegateのほうにabcinform()メソッドを作成。これも名前は何でも良い。プロトコルメソッドは必ず中身は記述しないようにする。引数は記述してよい。
またAbcクラスの中で何かしらのイベントが発生したときにdelegate.abcinform()でプロトコルメソッドを起動するように記述。
@obj protocol AbcDelegate { func abcinform() } class Abc { weak var delegate = AbcDelegate? ... //何かのイベント発生時にメソッドを起動 delegate.abcinform() }
ここまででイベントを通知するクラスとプロトコルの作成が出来た。
次にイベントの通知を受け取るクラス(ViewController)の中身を実装していく。
ViewControllerクラスでは、AbcDelegateを継承する。(AbcDelegateはクラスじゃなくプロトコルなので継承という表現が正しいかは不明)
私が入会しているテックキャンプではこの記述を「Delegateプロトコルを採用する」と表現していた。けど他の文献では継承と表現してるものもあったので、継承で良いかもしれない。
class ViewController: UIViewController, AbcDelegate {
これでViewControllerがAbcDelegateを継承した。
次にViewControllerクラスの中でAbcクラスのインスタンスを作り、abcクラスのプロパティ(delegate)にself(自分自身なのでViewController)を代入します。
class ViewController: UIViewController, AbcDelegate { override func viewDidLoad() { abc = Abc() abc.delegate = self }
これにより後で理解するがAbcクラスでのイベント通知先が自分自身になる、という寸法。
最後にプロトコルメソッドをViewControllクラスで再定義する。
class ViewController: UIViewController, AbcDelegate { override func viewDidLoad() { abc = Abc() abc.delegate = self } //Abcでイベントが発生したらabcinform()が起動することになってるので、 //このメソッドが起動。つまり通知が来るイメージ。 func abcinform() { //この中にイベントが発生したときにやらせたいことを記述する //abcクラスでイベントが発生! } }
これを遠目で眺めてると理解が進むかもしれない。
AbcクラスのプロパティであるdelegateはAbcDelegateとして宣言されているのに、ViewControllerクラス内でabc.delegate = selfとなってるのは型違いのように見えて違和感があるかもしれない。
これが出来る理由はdelegateプロパティはAbcDelegateで宣言されており、さらにAbcDelegateを継承してViewControllerを実装しているから、と理解している。
処理の流れを整理すると、Abcクラスの中でなにかのイベントが起こった際にdelegate.abcinform()が動くように実装している。
なんだけど実はこのdelegateプロパティはイコールselfであって、
abcinform()はViewController内で再定義されていて、結局Abcクラスでのイベントの発生後はViewControllerのabcinform()が動くようになる。これがAbcクラスでのイベント発生がViewController側にわたる通知の仕組みと理解している。
なので★でイベントが発生したときからの流れは以下のようになってるかもしれない。
おなじみTextFieldのDelegateを見てみる
Swift初学者が最初にデリゲートとプロトコルを学習すると言われているのがTextFieldです。
TextFieldではリターンキーが押されたというイベントをViewControllerは知ることができないため、UITextFieldDelegateプロトコルを継承してViewControllerを実装していくことになっていました。
これも上の図で説明できる動きをしています。
UITextFieldの定義部分を見てみましょう。[command]キーを押しながらUITextFieldの文字の上で左クリックしてJump to definitionへ進みます。
すると以下のようにUITextFieldの定義部分が見れます。
open class UITextField
を見ていくと下の方に、
weak open var delegate: UITextFieldDelegate? // default is nil. weak reference
とdelegateプロパティが定義されています。そしてこのプロパティの型ですがUITextFieldDelegateとなっています。
今度はUITextFieldDelegateを見てみます。
public protocol UITextFieldDelegate: NSObjectProtocol {
とあります。
そしてその中に
optional public func textFieldShouldReturn(_ textField: UITextField) -> Bool // called when ‘return’ key pressed. return No to ignore.
とメソッドが宣言されています。これはてTextFieldの中でリターンキーが押されたときにこのイベントが発生するのでした。
そうすると以下の記事のようにViewControllerクラスとUITextFieldDelegateを使って実装できる、というわけですね。