[swift入門]delegateとprotocolを実装するための簡単な方法

「delegate」って、正直よく分からない。

なので、調べてみると、あるクラスから他のクラスに処理を「移譲」するデザインパターンとのこと。

「移譲」すなわち「delegate」ってことらしいのだが、いまいちピンとこない。

delegateが必要なコードに出くわしたら、なんちゃらの.delegate = selfってやっときゃなんとかなるさ!

ってな状態だから一向に脱初心者できないw

 

これじゃダメだ!

ということで、備忘録も兼ねてこの記事を書くことにした次第です。

 

 

※素人のため勘違いや間違いがありましたら、ご指摘お願いします!

 

そもそもなぜ実装する必要があるの?

理解できないものは、避けた方が無難!

だから使わなきゃいいだけじゃんwって、そうも言ってられないのよ。

テキストフィールドやテーブルビューなんかを使うときには知らないふりできないし・・・。

ここで覚悟を決めて向き合うしかない!

 

ってことで、んじゃ、まずは「protocol」から

「delegate」と並んでよく語られるプロトコルさん!

 

こっちはデリゲートにくらべて理解が早かった。

 

プロトコルを調べてみると、「取り決め」という意味合いがあるらしい。

そして、プロトコルは「仕様書」みたいなものだそうだ。

 

仕様書っていわれてもなんの仕様書なのかよく分からないんだけど、

一言で片付けてしまうと「便利なもの」みたいだ。

 

というのも、必ず実装しなければならないものを事前に定義しておけるという点。

 

書き漏れがあったらビルドできないなど、

ないと困るものをプロトコルであらかじめ定義しておけば、

書き漏れが避けられる!

書かないとエラーがでるw

だから、プロトコルをみれば、それを採用しているクラスがどんなものなのか分かるといった優れものw

一見は百聞になんちゃらということで

プロトコルを使った簡単なアプリを作りながら説明をば!

 

 

delegateとprotocolの実装方法

 

先に大まかな流れを書いときます。

  1. プロトコルを作り、必要なプロパティ(値)やメソッド(関数)を定義する。
  2. プロトコルで定義したものを採用したクラスを作る。
  3. どのタイミングで、どのクラスに(プロトコルで定義したものを)実行してもらうか指定(デリゲート)する。
  4. 指定(デリゲート)したクラスに実行させる。
  5. 実行結果をviewに表示させ、ユーザーに提供する。

こんなところでしょうか?!

 

protocol作成

余談ですが、delegateを調べるとアウトソーシング(外注)みたいなもんって表現されているので、

今回作成するアプリは「見積を依頼する」といった内容のものにしたいと思いますw

 

そこで、プロトコルは

「洋服と靴と帽子を発注したら、いくらになるのか見積額を得る」

という内容にします。

我ながら発想が乏しいw

もっと洒落た感じにしたかったw

 

プロトコルを作り、必要なプロパティ(値)やメソッド(関数)を定義する。

では、プロトコルをFile -> New -> Fileから「Swift File」で新しく作ります。

File名は、邪道ですが「見積もり」にしちゃいましたw

 

見積もり.swift

Protocol.swift

分かりやすくしたかっただけなのですが、逆に分かりづらくなってしまった・・・。

面倒なのでこのまま行っちゃおう。

ちゃんと作るときは、「EstimateProtocol」とかにしたほうが良いに決まってるw

プロパティ名やメソッド名も同様!

 

ここでなにをやっているのかと言うと、

見積もりプロトコルを採用したクラスが必ず実装しなければならない

プロパティ(トータル金額)と

メソッド(洋服の金額、靴の金額、帽子の金額を計算するファンクション)を定義しました。

 

まず、6行目の

protocol 見積もり { }

でプロトコルを宣言?(この言い方は違うような気がする・・・)します。

見積もり」の部分には、任意のプロトコル名を書いてね!

プロトコル名のとなりの { } コードブロック内で

必要なプロパティとメソッドを定義していきます。

 

プロパティは12行目。

var 変数 { get set }

で定義します。

 

メソッドは、ここでは機能を定義しません。

func メソッド名(引数: 型) -> 型

 

 

プロトコルで定義したものを実行できるクラスを作る。

では次に実行役のクラスを作ります。

より安くより良いものをということで、

今回はA社とB社に納品額を争っていただくことにしましょう!

 

見積もり.swift ファイルを作ったように

File -> New -> Fileから「Swift File」を選択し、

ファイル名は、もちろん邪道な「A社」、「B社」!

 

A社.swift

A社.swift

 

ここでは、プロトコルで定義したプロパティとメソッドを実装しています。

もし仮に一つでも欠けてしまうと・・・

 

B社.swift

B社.swift

こんな感じでエラーで教えてくれます。

 

エラー内容:

Type ‘B‘ does not conform to protocol ‘見積もり

タイプ「B社」はプロトコル「見積もり」に準拠していません。とのこと

 

テーブルビューなんかもこんな仕組みになってるんだね。

こう見るとプロトコルってかなり有用なんだw

 

 

ここでは、まずclassを作りたいので、

class クラス名: プロトコル名 { } 

でさっき作ったプロトコルを採用したクラスを作ります。

{ } コードブロック内にプロパティとメソッドを書いていきましょう!

 

 

プロパティは、{ get set }を指定しているので、読み書きできるようにします。

var 変数: 型 {

      get {

      return total

      }

      set (変数) {

      total = 変数

      }

}

 

メソッドは、発注数を入力するとトータルの金額を計算するようにします。

func メソッド名(引数: 型) {

         値段 = 適当な値

         total = 値段 * 引数         

}

 

各メソッドで計算した値(total)を合わせたものをプロパティ(トータル金額)で得られるようにしています。

 

 

で、お次は一番分かりづらかったところ

どのタイミングで、どのクラスに(プロトコルで定義したものを)実行してもらうか指定(デリゲート)する。

プロトコル作って、そのプロトコルを採用したクラスを作るまではすんなり行けるけど、

この後どうすりゃ良いの?

 

 

ここが一番理解しづらかった部分w

 

 

とりま、発注先が2つ(A社、B社)できたので、どちらに見積もりをお願いするか決めるものを作ります。

その名も「発注先選択.swift」!

 

発注先選択.swift

発注先選択.swit

 

 

ここでは何をしているのかと言うと、

デリゲートでクラス(さっき作ったA社、B社)を選択できるようにして、

プロトコルで定義したメソッドを実行し、見積額を提示させる見積もり金額メソッドを作っています。

 

7行目の

var delegate: 見積もり?

で、実行するプロトコルを決めます。

プロトコルを採用するって言い方でいいのかな?

 

 

発注先選択.delegate =  クラス名

で、デリゲート(見積を出す会社を指定)できるようにしています。

このデリゲートを指定せず、見積もりメソッドを実効すると

デバッグエリアに「今回は見送ります。」を表示させるようにしています。

 

デバッグエリアとは、Xcodeのコードを書く部分の下ね!

デバッグエリア

 

ViewControllerのviewDidLoadの { } コードブロック内に

デリゲートを指定しないで

発注先選択.swiftのメソッド(発注選択.見積金額() )を実行すると

デバッグエリアに「今回は見送ります。」って表示されます。

 

つぎは、このViewControllerのviewDidLoadの { } コードブロック内で

デリゲートを指定(発注選択.delegate = a社)してあげると

デバックエリアにデリゲートで指定したクラスの見積もり額が表示されます。

 

無事A社に見積もりをさせることができました。

金額が0なのは、クラスのメソッドの引数(洋服の数など)を入力していないので、

引数には初期値の0が入ってしまっているためと、

デバッグエリアに0が2つ表示されてしまっているのは、

発注先選択.swiftの22行目のprint( )メソッドと

ViewControllerの36行目のprint( )メソッドが働いているためです。

 

 

どのタイミングで、どのクラスに(プロトコルで定義したものを)実行してもらうか指定(デリゲート)する。の答えは、

  1. viewの読み込み時や画面がタップされた、ボタンが押されたなどされ、それに対して欲しい反応結果を得たいとき
  2. 実行してもらいたいクラスをデリゲートで指定
  3. 実行してもらいたいメソッドを書く

これさえ押さえればもうプロトコルとデリゲートはこわくないw

 

ここで終わってもいいんだけど、

せっかくだから残りをちゃっちゃと片付けちゃいましょう。

 

storyboard

チョーてきと〜だけど、

こんな感じにしました。

storyboard

オートリサイジングなどでレイアウト指定はまったくしてませんw

 

テキストフィールドに発注する数を入力し、

「見積もりを依頼するボタン」をタップすると

見積金額がラベルに表示されるといった簡単なもの。

 

この辺の説明は割愛しちゃいます。

 

で、

ビューコントローラ

 

ViewController.swift

ViewController.swift

 

 

まず、storyboardのパーツ(ラベルやボタンなど)をViewControllerに関連付けます。

 

で、テキストフィールドを使いたいので、

テキストフィールドのプロトコル(6行目 UITextFieldDelegate)を採用します。

で、ビューの読み込み時にテキストフィールド内の変化(文字が入力されたなど)があった際に

その変化を表示させるなどを実行させるクラスを指定(デリゲート)します。

今回は.selfでViewContorollerを指定しています。

テキストフィールドは今回3つあるので、

それぞれデリゲートします。

洋服の数.delegate = self

靴の数.delegate = self

帽子の数.delegate = self

 

さっきは、発注先選択.swiftの7行目で自分で作ったプロトコルを採用しましたね。

発注先選択.swit

 

次に採用したテキストフィールドのデリゲートの内容を書いていきます。

(37行目〜54行目はちょっと飛ばしちゃいます。)

 

ViewController.swift

今回必要なテキストフィールドのメソッドは3つ

 

テキストフィールドに入力時、キーボードのreturnが押されたら呼ばれるメソッド

下のコードではキーボードを下げています。

func textFieldShouldReturn(_ textField: UITextField) -> Bool {

view.endEditing(true)

return false

}

 

テキストフィールドの内のクリアボタン(✗ボタン)が押されたら呼ばれるメソッド

func textFieldShouldClear(_ textField: UITextField) -> Bool {

        return true

}

 

テキストフィールドの値が変化したときに呼ばれるメソッド

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {

return true

}

 

テキストフィールドのメソッドの { } コードブロック内のコードは

ちょっといい加減に書いていますが、

この辺はこの記事の趣旨とズレるので説明などは省略しちゃいますw

 

ここで大事なのは、

先ほど飛ばした37行目〜54行目!

ViewController.swift

「見積もりを依頼するボタン」が押されたら呼ばれる

メソッドとそれを実行するクラス。

 

では、順番に説明していきます。

発注選択.delegate?.トータル金額 = 0

まずトータル金額を0円にしてトータル金額に保持されている金額を取り除いています。

 

大事なのは次から

発注選択.delegate = a社 (もしくは b社)

だれ(クラス)に実行させるか指定(デリゲート)します。

 

で、実行してほしいこと(メソッド)を書きます。

発注選択.見積もり金額( )

 

あとは、実行して得られた値を取り出して、テキストフィールドに表示させるだけです。

guard let 見積金額 = 発注選択.delegate?.トータル金額 else { return }

B社見積額.text = String(見積金額)

 

 

最後に

じゃ、最後におさらいです。

 

大事なのは、

  • いつ、どのタイミングで
  • だれに実行してほしいのか指定(delegate)し
  • 実行内容(protocolを採用したクラスのメソッド)を書く

 

なんとなくおわかりいただけたでしょうか?

この記事を読んでくれた方のデリゲートとプロトコルの理解に、

少しでもお役に立てたのならば嬉しいです。

 

 

 

最後までありがとうございました!

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

CAPTCHA