【SwiftUI】TextField付きAlertを表示する

今回は、以下のような TextField(SecureField) を備えた Alert を作って見たいと思います。

こちらの記事を参考に作成しました。

TextField付きAlertを表示する

残念ながら、2021/01/08 時点で SwiftUI の純粋な機能では Alert 自体に TextField を追加するプロパティはありません。

そのため、例に漏れず UIViewControllerRepresentable プロトコルを継承して、UKit の UIAlertController をラップしたViewを作成することになります。

早速、作成したコードを紹介します。

TextFieldAlertView

import SwiftUI

struct TextFieldAlertView: UIViewControllerRepresentable {
    
    @Binding var text: String
    @Binding var isShowingAlert: Bool
    
    let placeholder: String
    let isSecureTextEntry: Bool
    let title: String
    let message: String
    
    let leftButtonTitle: String?
    let rightButtonTitle: String?
    
    var leftButtonAction: (() -> Void)?
    var rightButtonAction: (() -> Void)?
    
    func makeUIViewController(context: UIViewControllerRepresentableContext<TextFieldAlertView>) -> some UIViewController {
        return UIViewController()
    }
    
    func updateUIViewController(_ uiViewController: UIViewControllerType, context: UIViewControllerRepresentableContext<TextFieldAlertView>) {
        
        guard context.coordinator.alert == nil else {
            return
        }
        
        if !isShowingAlert {
            return
        }
        
        let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
        context.coordinator.alert = alert
        
        alert.addTextField { textField in
            textField.placeholder = placeholder
            textField.text = text
            textField.delegate = context.coordinator
            textField.isSecureTextEntry = isSecureTextEntry
        }
        
        if leftButtonTitle != nil {
            alert.addAction(UIAlertAction(title: leftButtonTitle, style: .default) { _ in
                alert.dismiss(animated: true) {
                    isShowingAlert = false
                    leftButtonAction?()
                }
            })
        }
        
        if rightButtonTitle != nil {
            alert.addAction(UIAlertAction(title: rightButtonTitle, style: .default) { _ in
                if let textField = alert.textFields?.first, let text = textField.text {
                    self.text = text
                }
                alert.dismiss(animated: true) {
                    isShowingAlert = false
                    rightButtonAction?()
                }
            })
        }
        
        DispatchQueue.main.async {
            uiViewController.present(alert, animated: true, completion: {
                isShowingAlert = false
                context.coordinator.alert = nil
            })
        }
    }
    
    func makeCoordinator() -> TextFieldAlertView.Coordinator {
        Coordinator(self)
    }
    
    class Coordinator: NSObject, UITextFieldDelegate {
        
        var alert: UIAlertController?
        var view: TextFieldAlertView
        
        init(_ view: TextFieldAlertView) {
            self.view = view
        }
        
        func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
            if let text = textField.text as NSString? {
                self.view.text = text.replacingCharacters(in: range, with: string)
            } else {
                self.view.text = ""
            }
            return true
        }
    }
}

引数(プロパティ)

6個〜10個の引数を指定できるようにしました。

  • text:入力文字列とバインディングする状態変数(@State or @Publish)を指定します
  • isShowingAlert:表示トリガーとなる状態変数(@State or @Publish)を指定します
  • placeholder:プレースホルダーを指定します
  • isSecureTextEntry:文字を●表示する場合は true にします
  • title:タイトルを指定します。
  • message:タイトルのしたに表示するメッセージを指定します
  • leftButtonTitle:左側ボタンの文字列を指定します(任意
  • rightButtonTitle:右側ボタンの文字列を指定します(任意
  • leftButtonAction:左側ボタンの押下時のアクションを指定します(任意
  • rightButtonAction:右側ボタンの押下時のアクションを指定します(任意

使用例

struct ContentView: View {
    
    @State private var isShowingAlert = true
    @State var text: String = ""
    
    var body: some View {
        Button("タップしてアラート表示") {
            isShowingAlert = true
        }
        TextFieldAlertView(
            text: $text,
            isShowingAlert: $isShowingAlert,
            placeholder: "",
            isSecureTextEntry: true,
            title: "ログイン",
            message: "パスワードを入力してください",
            leftButtonTitle: "キャンセル",
            rightButtonTitle: "認証",
            leftButtonAction: nil,
            rightButtonAction: {
                print("パスワード認証リクエスト")
            }
        )
    }
}

一例ですが、セキュアな処理実行前のユーザー再認証(パスワード入力)などに使えると思います。

筆者は Firebase のパスワード変更リクエストの際に再認証要求が返ってきた場合に表示する、という用途に利用しました。

以上

1件のコメント

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です