ボクココ

少数サービス開発運用に関するテックブログ

SwiftUI で快適なログインUIを作ろう

ども、@kimihom です。

f:id:cevid_cpp:20220326144057j:plain

SwiftUI を使ってログインをより快適にする対応をしたので、その実施内容についてまとめておこう。

テキストフォーカス

ログインをしようとするページの場合、ほぼ必ず最初はメールアドレスなどの入力から始まることだろう。iOS の場合、フォーカスが当たってない状態でタイプしても反映されない形となる。

最初からフォーカスが当たっているのは、利用ユーザーのことを考えた改善と言える。

さて、このフォーカスだけど SwiftUI でできるようになったのは iOS 15.0 からである。focused) が登場した。

struct SigninView: View {
    enum Field: Hashable {
        case email
        case password
    }

    @State private var email = ""
    @State private var password = ""
    @FocusState private var focusedField: Field?

    var body: some View {
        ZStack {
            TextField("メールアドレス", text: $email)
                .keyboardType(.emailAddress)
                .focused($focusedField, equals: .email)
                .onAppear(perform: {
                    DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
                        self.focusedField = .email
                    }
                })
        }
    }
}

onAppear でUIが表示がされた際の実行として、focusedField をセットする。ここで 少し時間を置いているのは、現状の iOS だとロードされた後すぐにフォーカスを当てることはできないためである。 アプリを起動した瞬間にログイン画面を表示させる場合に、この対応が必要となる。起動直後はフォーカスが当たらず、0.5秒たった後にキーボードが開くようになる。

その後、パスワード入力もして"ログイン"ボタンを押した後、仮にパスワードミスなどでフォーカスを戻したい時は、

self.focusedField = .email

を指定するだけで、メールアドレスにフォーカスが当たるようになり、キーボードが勝手に出てくるようになる。

パスワード表示

iPhone のような小さな端末だと、今パスワードが正しく入力できているかを確認したい時があるようだ。私としては生でパスワードを見せるユーザー経験はあまりいいとは思わないが、長いパスワードを入力し直すのが面倒という方はパスワード内容を確認しながら入力した方が良いようである。多くのメジャーアプリでも実装されているようだ。

さて、この実装はパスワード入力のフォーマットSecureField に加えて、TextField で必要な時に書き換えることで実現が可能だ。

struct SigninView: View {

    @State private var password = ""
    @State private var secured: Bool = true

    var body: some View {
        ZStack {
            HStack {
                if secured {
                    SecureField("パスワード", text: $password)
                        .focused($focusedField, equals: .password)
                } else {
                    TextField("パスワード", text: $password)
                        .focused($focusedField, equals: .password)
                }
                Button(action: {
                    self.secured.toggle()
                    DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
                        self.focusedField = .password
                    }
                }) {
                    if secured {
                        Image(systemName: "eye.slash")
                    } else {
                        Image(systemName: "eye")
                    }
                }
                .frame(width: 50)
        }
    }
}

デフォルトは secured = true の状態なのでSecureField が利用され、パスワード表示ボタンをタップすると、secured = false となってそのタイミングでパスワードがテキストで表示されるようになる。

パスワードの入力を SecureField から TextField に変わったタイミングで、フォーカスが外れてしまう。そのため、改めて password に対してフォーカスが当たるような実装をしている。SecureFieldとTextFieldで同じ password 指定で大丈夫かと思われるかもしれないが、 if~else でちゃんと出し分けているので問題ない。

最終的にログインボタンを押した際には、self.secured = true に戻して表示を隠しておいた方が、ユーザー体験は良さそうである。

終わりに

最初慣れるまでは違和感の多い SwiftUI。慣れてくるとその便利さに気づくことができよう。

私の実装の場合、よりセキュアに2段階認証の実装も加わっている。さらに、新しい実装方法として顔認証や指紋認証での実装も増えてきている。今後はよりセキュアにログインできるように、これらの実装も検討していきたい。