SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

Swift 4で最初に知っておきたい3つのポイント

Swift 4におけるオブジェクト間での相互作用

Swift 4で最初に知っておきたい3つのポイント 第3回

  • X ポスト
  • このエントリーをはてなブックマークに追加

iOSアプリ開発でよく利用されるデリゲート

 iOSアプリ開発では、Objective-C/Swiftに共通する概念としてデリゲートがあります。デリゲートとは、他のオブジェクトから利用されるためのメソッドのことです。特定のオブジェクトから利用されるデリゲートのメソッドをまとめたものがプロトコルで、プロトコルはクラスとして存在します。iOSアプリ開発で最もつまづきやすい点が、このデリゲートを利用する部分です。デリゲートの概念の整理も兼ねて、デリゲートとプロトコルの具体的な利用例をサンプルを用いて説明します。

デリゲートを利用する手順

 Swiftでは入力欄やピッカーなどのUI部品において、あらかじめプロトコルとしてのクラスが用意されており、UI部品の動作に応じたデリゲートのメソッドがそろっています。そのため、UI部品の決まった動きを利用するだけであれば、既存のデリゲートの処理内容のみを記述することで、アプリの動きを実装できます。ビューコントローラーに配置した、UI部品のデリゲートを利用する手順を簡単にまとめると次の図のようになります。

デリゲートを利用する手順
デリゲートを利用する手順
  1. ビューコントローラーでUI部品のプロトコルを実装し、デリゲートを利用できるようにします。
  2. UI部品側からはdelegateプロパティで利用する、デリゲートを実装したビューコントローラーを指定します。
  3. デリゲートのメソッドをオーバーライドして処理内容を記述します。
  4. アプリがUI部品の動作に応じた処理内容を行うことを確認します。

 iOSにおける、UI部品の画面をタッチした/スクロールした/選択したなどの動きは、すべてこのデリゲートを利用して実装します。サンプルで実際に確認してみます。

UITextFieldDelegateプロトコルを利用して入力欄の入力開始/終了の処理を行う

 入力欄を作成するUITextFieldクラスとUITextFieldDelegateプロトコルを使い、既存のデリゲートの動きを確認します。先述した手順の1から3を実装してみます。

リスト10 TextFieldViewController.swift
class TextFieldViewController: UIViewController {

    @IBOutlet weak var textField: UITextField!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // 利用するデリゲートを指定
        self.textField.delegate = self // ------(2)
    }
}

// UITextFieldDelegateを実装
extension TextFieldViewController: UITextFieldDelegate {  // ------(1)
    
    // 編集開始時の処理
    func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool { // ------(3)
        // UItextFieldの枠を赤に
        textField.layer.borderColor = UIColor.red.cgColor
        textField.layer.borderWidth = 1.0
        return true
    }
    
    // リターンキーを押した際の処理
    func textFieldShouldReturn(_ textField: UITextField) -> Bool { // ------(3)
        // UItextFieldをアンフォーカス
        textField.resignFirstResponder()
        // 枠の太さを0に
        textField.layer.borderWidth = 0.0
        return true
    }
}

 サンプルでは前節の手順通りに実装しています。
 アプリを起動すると次の動きが確認できます。

  • (1)エクステンションを使ってUITextFieldDelegateプロトコルをビューコントローラーに実装します。
  • (2)UITextFieldオブジェクトのdelegateプロパティで、UITextFieldDelegateプロトコルにビューコントローラーであるselfを指定します。
  • (3)デリゲートのメソッドをオーバーライドして処理内容を記述します。編集開始時にレイヤーの枠を赤にして太さを1.0にします。リターンキーを押した際には入力欄からフォーカスを外し、レイヤーの枠の太さを0にします。
デリゲートによる入力欄の編集開始/終了時の処理
デリゲートによる入力欄の編集開始/終了時の処理

 既存のデリゲートの処理では、このようにUI部品のクラスとペアになったプロトコルを利用してUI部品の動作時の処理を行います。

デリゲートを自作する【Swift 3】

 前節のサンプルの通り、デリゲートは呼び出しと処理内容を任意で指定できる非常に汎用性の高い機能です。デリゲートは、クラスと同様に開発者が任意に作成することができます。Swift 3以降は、次の2点によってデリゲートをより容易に扱えるようになりました。

  • プロトコルの宣言時に、必ずしも親クラスのNSObjectProtocolクラスを継承する必要がない。
  • デリゲートのメソッドを実行する際に、実装関係やメソッドの存在確認の必要がなくなった。

 サンプルを作成しながら具体的に説明します。

作成するデリゲートと処理の流れ

 下図の通り、モーダルで表示したテーブルメニューから選択された項目を元の画面に表示するサンプルを作成します。

作成するサンプル
作成するサンプル

 「テーブルで選択された項目を元画面に表示する処理」をデリゲートを通して行います。

プロトコルを宣言

 デリゲート/プロトコルを作成する場合は、クラスやメソッドを作成する際と同様に任意の名前を利用することができます。今回は選択された項目を渡すだけなので、MenuTableViewControllerクラスを同じファイル内で次の通りに宣言します。

リスト11 MenuTableViewController.swift
protocol MenuDelegate {
    func didSelect(_ word:String)
}

 Swiftの仕様には、プロトコルの親クラスに相当するNSObjectProtocolクラスと呼ばれるクラスが存在します。以前は、プロトコルの宣言時にNSObjectProtocolクラスを継承しないと、プログラムが正常に動作しませんでした。Swift 3以降は「protocol」の宣言があれば、それだけでコンパイラはプロトコルとして解釈して実行します。NSObjectProtocolクラスの機能を利用しない単純なプロトコルであれば、サンプルのように簡易的に宣言できます。
 サンプルではMenuDelegateという名前でプロトコルを宣言しています。MenuDelegateプロトコルの中で、項目が選択された際に実行されるメソッドをdidSelect(_:)の名前で定義します。引数は文字列型の変数です。デリゲートなのでメソッドの内容は記述しません。

delegateプロパティで作成したプロトコルを指定

 作成したプロトコルをdelegateプロパティでアクセスできるようにします。今回はMenuTableViewControllerオブジェクトがプロトコルを持つので、MenuTableViewControllerクラス内にdelegateプロパティを設け、型をMenuDelegateプロトコルに指定します。

リスト12 MenuTableViewController.swift
class MenuTableViewController: UITableViewController {
    
    var delegate: MenuDelegate?
...後略...

 デリゲートを使わない場合も考えられるため、後ろに「?」をつけてnullを許可するオプショナル型として宣言します。オプショナル型として宣言することで、実行時にプロトコルを実装済み、もしくはメソッドが存在するか判定する処理は不要となります。もし仮にプロトコルが未実装/メソッドが存在しないということがあっても、オプショナル型で宣言しておけばエラーでプログラムが止まる事態を回避できます。

デリゲートを実行する

 MenuTableViewControllerクラス内では、delegateプロパティを設けるだけでなく、デリゲートを実行できるようにします。サンプルではテーブルのセルが選択された後に、プロトコル内のdidSelect(_:)メソッドを実行してセルの項目を渡します。

リスト13 MenuTableViewController.swift
// テーブルのセル選択時の処理
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    // IndexPathからセルを取得
    let cell = tableView.cellForRow(at: indexPath)    // ------(1)
    if let text = cell?.textLabel?.text {
        self.delegate?.didSelect(text)    // ------(2)
    }
    // モーダルを閉じる
    self.dismiss(animated: true, completion: nil)
}

 テーブルのセルが選択された際には、UITableViewDelegateプロトコルのtableView(_:didSelectRowAt:)メソッドが呼ばれます。プロトコル内のdidSelect(_:)メソッドを記述するのはtableView(_:didSelectRowAt:)メソッドの中です。

 選択されたセルは、tableView(_:didSelectRowAt:)メソッドで渡されるIndexPathオブジェクトから得られます(1)。得られたセルはtitleLabelの値が項目名なので、この値をdidSelect(_:)メソッドに渡して実行します(2)。これでモーダルで表示されるメニュー側の処理は終わりです。

プロトコルを実装する

 作成したMenuDelegateプロトコルをビューコントローラーに実装します。前節のサンプルと同様に、エクステンションを利用します。

リスト14 BaseViewController.swift
extension BaseViewController: MenuDelegate {
    func didSelect(_ word: String) {
        self.label.text = "\(word)が選択されました!"
    }
}

 実装したプロトコルの中で、メソッドをオーバーライドして処理内容を記述します。ここではメソッドに渡された項目をラベルに表示する処理を行っています。実装したプロトコルを利用するためには、delegateプロパティでプロトコルを実装した自分自身を指定することを忘れないでください。

リスト15 MenuTableViewController.swift
@IBAction func onPressMenu(_ sender: Any) {
    // Main.storyboardを取得
    let storyBoard = UIStoryboard(name: "Main", bundle: nil)
    if let vc = storyBoard.instantiateViewController(withIdentifier:"MenuTableViewController") as? 
     MenuTableViewController {
        vc.delegate = self            // delegateを指定
        self.present(vc, animated: true, completion: nil)
    }
}

 MenuTableViewControllerクラスのインスタンスを生成する際に、delegateプロパティでselfを指定します。サンプルを実行すると次の動作が確認できます。

サンプルの実行結果
サンプルの実行結果

まとめ

 今回はSwiftでよく利用される画面上の処理と、それに関わる新しい文法を紹介しました。画面間でのデータのやり取りはアプリを設計する際にも非常に重要です。次回は、今回解説した方法以外の動的なプロパティの値の変更手段や、クロージャを使った画面上の処理について説明する予定です。

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
Swift 4で最初に知っておきたい3つのポイント連載記事一覧

もっと読む

この記事の著者

WINGSプロジェクト 片渕 彼富(カタフチ カノトミ)

WINGSプロジェクトについて>有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。2018年11月時点での登録メンバは55名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂きたい。著書記事多数。 RSS X: @WingsPro_info(公式)、@WingsPro_info/wings(メンバーリスト) Facebook

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

山田 祥寛(ヤマダ ヨシヒロ)

静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるReact実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/10598 2018/01/12 14:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング