[iOS14] UIAlertController内のUITextFieldに文字が入力されていない場合はUIAlertActionを選択できないようにする


環境

  • Xcode 12.4
  • Swift 5.3.2

内容

iPhoneのPhotosアプリ内でアルバムを新規作成する際、
以下のようなアラートが表示されます。

Photos

初めは UIAlertActionSave アクションが選択できないようになっています。
UIAlertController 内の UITextField に文字を入力することで、
先のアクションが選択できるようになります。

今回はこちらを2つの方法で実現してみます。

下準備

適当にラベルとボタンを配置します。
ラベルはアラートで入力した文字列を表示するために用意しています。

SampleViewController

実装

①愚直に実装

SampleViewController.swift
final class SampleViewController: UIViewController {
    private let label = UILabel()
    private let button = UIButton(type: .system)

    private var saveAction: UIAlertAction!

    /*
    label, button等のUI処理
    */

    // ボタン押下時のアクション
    @objc private func showTextFieldAlert() {
        let alert = UIAlertController(title: "New Label",
                                      message: "Enter a name for the label.",
                                      preferredStyle: .alert)
        // textField
        alert.addTextField(configurationHandler: { [weak self] textField in
            textField.placeholder = "Label text"
            textField.delegate = self
        })
        // cancel action
        let cancelAction = UIAlertAction(title: "Cancel",
                                         style: .cancel)
        alert.addAction(cancelAction)
        // save action
        saveAction = UIAlertAction(title: "Save",
                                   style: .default,
                                   handler: { [weak alert] _ in
                                    let inputText = alert?.textFields?.first?.text
                                    self.label.text = inputText
                                   })
        saveAction.isEnabled = false
        alert.addAction(saveAction)

        present(alert, animated: true)
    }
}

extension SampleViewController: UITextFieldDelegate {
    func textFieldDidChangeSelection(_ textField: UITextField) {
        saveAction.isEnabled = !(textField.text?.trimmingCharacters(in: .whitespaces) ?? "").isEmpty
    }
}

改善したい点

  • 制御したいアクションをクラスのメンバ変数として用意している
  • ViewControllerに別のUITextFieldを用意する場合、Delegateのメソッド内でUITextFieldの判別をする必要がある

こちらを踏まえた上で別の方法に進みます。

②UIActionを活用

iOS14から追加された以下のプロパティとメソッドを利用します。
- Apple Developer Document - sender
- Apple Developer Document - addAction(_:for:)

SampleViewController.swift
final class SampleViewController: UIViewController {
    private let label = UILabel()
    private let button = UIButton(type: .system)

    /*
    label, button等のUI処理
    */

    // ボタン押下時のアクション
    @objc private func showTextFieldAlert() {
        let alert = UIAlertController(title: "New Label",
                                      message: "Enter a name for the label.",
                                      preferredStyle: .alert)
        // cancel action
        let cancelAction = UIAlertAction(title: "Cancel",
                                         style: .cancel)
        alert.addAction(cancelAction)
        // save action
        let saveAction = UIAlertAction(title: "Save",
                                       style: .default) { [weak alert] _ in
            self.label.text = alert?.textFields?.first?.text
        }
        saveAction.isEnabled = false
        alert.addAction(saveAction)
        // textField
        let action = UIAction(handler: { action in
            guard let textField = action.sender as? UITextField else { return }
            saveAction.isEnabled = !(textField.text?.trimmingCharacters(in: .whitespaces) ?? "").isEmpty
        })
        alert.addTextField() { textField in
            textField.placeholder = "Label text"
            textField.addAction(action, for: .editingChanged)
        }

        present(alert, animated: true)
    }
}

これで①の改善したい点を解決できたかと思います。

実際の動作