[iOS] 「TableViewで入力フォームを作成する場合の実装」を簡単にしてくれるライブラリ「Eureka」を試してみた

ios_ui

はじめに

こんばんは。モバイルアプリサービス部の平屋です。

「TableViewで入力フォームを作成する場合の実装」を簡単にしてくれるライブラリ「Eureka」を試してみましたので、その内容を紹介します。

「Eureka」を使用すれば、以下のスクショのような入力フォーム画面を簡単に作成できます。

検証環境

  • macOS Sierra Version 10.12.5
  • Xcode Version 8.3.2

目次

  • 導入
  • Eurekaを構成する主要コンポーネント
  • 入力フォーム画面用のViewControllerの作成
  • Section/Rowの追加
  • Rowの値の取得
  • Callback
  • Section HeaderとFooterの設定
  • 入力値のバリデーション

導入

CocoaPodsやCarthageなどを使用して導入できます。

// Podfileの作成例

use_frameworks!

target "EurekaSample" do
  pod 'Eureka'
end

Eurekaを構成する主要コンポーネント

FormViewControllerのformプロパティに対して、SectionやRowを追加することによって、入力フォームを作成できます。

Form

  • Eurekaの中心となるクラス
  • 入力フォームを表す
  • SectionやRowなどを管理する

FormViewController

  • 入力フォーム画面を管理する
  • UIViewControllerのサブクラス
  • 以下のようなプロパティを持つ
    • var form: Form
    • var tableView: UITableView?

Section

  • セクションを表す
  • Rowを持つ

Row

  • 行を表す
  • 以下を持つ
    • Value(入力/選択された値など)
    • Cell(UITableViewCellのサブクラス)
  • 入力フォーム作成時には、Rowのタイプごとに用意されているサブクラスを使用する
    • 利用可能なRowの一覧
      • TextRow: UITextfieldを持つRow
      • Switch Row: UISwitchを持つRow
      • Date Rows: UIDatePickerを持つRow

入力フォーム画面用のViewControllerの作成

FormViewControllerのサブクラスを作成します。

import UIKit
import Eureka

class ViewController: FormViewController {
    // ...
}

Section/Rowの追加

カスタム演算子+++を使用してSectionを、<<<を使用してRowを追加できます。

class ViewController: FormViewController {
    override func viewDidLoad() {
        // ...

        form
            +++ Section()
            <<< NameRow() {
                $0.title = "Name"
            }
    }
}

カスタム演算子を使いたくない場合は、appendメソッドなどを使用してSection/Rowを追加できます。

class ViewController: FormViewController {
    override func viewDidLoad() {
        // ...

        let row = NameRow() { 
            $0.title = "name" 
        }
        let section = Section()
        section.append(row)
        form.append(section)
    }
}

Rowの値の取得

Row作成時にタグを設定しておけば、あとから値を取得することができます。

NameRow("NameRowTag")

rowBy(tag:)メソッドを使用する

タグと共にFormのrowBy(tag:)メソッドを使用すれば、対応するRowを取得し、Rowから値を取得することができます。

// Rowの値を取得する
let nameRow = form.rowBy(tag: "NameRowTag") as! NameRow
let name = nameRow.value!

values()メソッドを使用する

Formのvalues()メソッドを使用すれば、タグが設定されている全てのRowの値を取得できます。

// タグ設定済みの全てのRowの値を取得
let values = form.values()

// Rowの値を取得する
let name = values["NameRowTag"] as! String

Callback

値変更時、Cell選択時などに実行したい処理があれば、処理をCallbackとしてRowに追加できます。

利用可能なCallback一覧はこちらにあります。

以下の例では、Rowの値が変更されたタイミングでログを出力しています。

form
    +++ Section()
    <<< NameRow() {
        // ...
    }.onChange {
        print("Name changed:", $0.value ?? "");
    }

Section HeaderとFooterの設定

Section作成時に、HeaderやFooterのタイトルを渡すと、タイトル付きのSectionを作成できます。

// Headerのタイトルだけを設定する
Section("Title")

// HeaderとFooterのタイトルを設定する
Section(header: "Title", footer: "Footer Title")

// Footerのタイトルだけを設定する
Section(footer: "Footer Title")

入力値のバリデーション

入力値のバリデーションを実行したい場合は、Rowにバリデーションのルールやオプションを追加します。バリデーション機能の詳細はこちらに記載されています。

以下の例では、NameRowに入力必須のルールを追加しています。値変更時に値が空だった場合、タイトルラベルの色が赤になります。

form
    +++ Section()
    <<< NameRow("NameRowTag") {
        // ...

        $0.add(rule: RuleRequired())
        $0.validationOptions = .validatesOnChange
    }.cellUpdate { cell, row in
        if !row.isValid {
            cell.titleLabel?.textColor = .red
        }
    }

また、Formのvalidate()メソッドを使用すれば、入力フォーム全体に対してバリデーションを実行できます。

func didTapSaveButton(sender: UIBarButtonItem) {
    let errors = form.validate()
    guard errors.isEmpty else {
        print("validate errors:", errors)
        return
    }

    // ...
}

実装例

今回は、会員登録画面っぽいサンプルを作ってみました。

コードは以下のリポジトリで公開してます。

さいごに

本記事では、入力フォーム作成支援ライブラリ「Eureka」の概要を紹介しました。

テキストフィールドを持つセル、日付選択を提供するセルなどの、入力フォームを構成するセルをゼロから作るのは地味に手間がかかるので、とても便利だなと思いました!

本記事で紹介した機能以外にも、Eurekaは以下のような機能を提供してくれます。気になった方は、実際に試してみると良いかと思います。

  • 複数の項目から選択させるためのセクションを作成する
  • カスタムのセクションヘッダを追加する
  • SectionやRowの表示/非表示切り替えを動的に行う
  • 項目の追加、削除、並び替えを実行できるセクションを作成する
  • カスタムのRowを作成する

参考資料

AWS Cloud Roadshow 2017 福岡