概要
この記事では、AndroidのTextInputLayoutのように、エラー表示付きの入力フォームを作る方法について説明します。ついでに、入力値のバリデーションも行います。
サンプル
Text-Input-Layout@githubに動作するプロジェクトがあります。
仕組み
TextInputView
タイトルラベル、必須マークラベル、テキストフィールド、エラーラベルを持つカスタムビューとTextInputViewを用意します。
クラス全体をIBDesignable、title, isRequired, placeholder, errorMessageを@IBInspectableとし、Interface Builderから値を入力できるようにします。 また、computed propertyとして、isValidatedを宣言します。isValidatedを呼ぶと、バリデーションを行い、必要に応じてエラーを表示します。 これにより、呼び出し元は、TextInputValidationTypeを最初に設定すれば、バリデーションロジックやエラーの表示・非表示を気にする必要はありません。
enum TextInputValidationType: Int { case none case email } @IBDesignable class TextInputView: UIView { @IBOutlet fileprivate weak var titleLabel: UILabel! @IBOutlet fileprivate weak var titleViewWidthConstraint: NSLayoutConstraint! @IBOutlet fileprivate weak var requiredLabel: UILabel! @IBOutlet weak var textField: UITextField! @IBOutlet fileprivate weak var errorLabel: UILabel! @IBInspectable var title: String = "" { didSet { titleLabel?.text = title titleLabel?.sizeToFit() titleViewWidthConstraint?.constant = titleLabel.frame.width } } @IBInspectable var isRequired: Bool = true { didSet { requiredLabel?.isHidden = !isRequired } } @IBInspectable var placeholder: String = "" { didSet { textField?.placeholder = placeholder } } @IBInspectable var errorMessage: String = "" { didSet { errorLabel?.text = errorMessage } } var validationType: TextInputValidationType = .none var text: String { return textField?.text ?? "" } var isValidated: Bool { errorLabel.isHidden = true switch validationType { case .email: errorLabel.isHidden = isEmail(text: text) default: break } if text.isEmpty { errorLabel.isHidden = !isRequired } return errorLabel.isHidden } }
TextInputViewの使い方
Interface Builder上でUIViewを追加し、ClassをTextInputViewに設定します。 そうすると、IBInspectableを設定したプロパティをInterface Builder上から操作できることがわかります。isRequiredをOn(true)にすると、(Required)が表示されていることがわかります。
逆にisRequiredをOff(false)にすると、(Required)が非表示になったことがわかります。
呼び出し元では、validationTypeやtextFieldのキーボードタイプを指定します。 また、isValidatedを呼び出すことで、バリデーション条件をクリアしているかを知ることができます。 このように、TextInputView側にバリデーションやエラー表示の処理を任せることで、呼び出し元は、スッキリとしたコードとなります。 (ちなみに、IBInspectableに、enum型を指定することができれば、より簡易に書けそうですが、現状はできません。)
class ViewController: UIViewController { @IBOutlet fileprivate weak var emailTextInputView: TextInputView! @IBOutlet fileprivate var textInputViews: [TextInputView]! @IBOutlet fileprivate weak var resultLabel: UILabel! override func viewDidLoad() { super.viewDidLoad() emailTextInputView.validationType = .email emailTextInputView.textField.keyboardType = .emailAddress emailTextInputView.textField.returnKeyType = .done } @IBAction private func onValidationButtonClick(_ sender: UIButton) { let areValidated = !textInputViews.map({ $0.isValidated }).contains(false) resultLabel.text = areValidated ? "Validated :)" : "Invalidated :(" } }
サンプル
Text-Input-Layout@githubに動作するプロジェクトがあります。