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

# 環境
- Xcode 12.4
- Swift 5.3.2

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

|Photos|
|:---:|
|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/476381/19dc7488-9de2-b857-5801-0f7824476df2.gif" width=400>|

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

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

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

| SampleViewController |
|:---:|
|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/476381/6418418e-c512-9627-c198-a155c49c23c2.png" width=400>|

# 実装
## ①愚直に実装

```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](https://developer.apple.com/documentation/uikit/uiaction/3600315-sender)
- Apple Developer Document - [addAction(_:for:)](https://developer.apple.com/documentation/uikit/uicontrol/3600490-addaction)

```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)
    }
}
```

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


# 実際の動作
||
|:---:|
|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/476381/600ed40b-b42e-4bd4-a8a8-67d9085ea15a.gif" width=400>|