[SwiftUI]パスワード入力欄の作り方
入力内容の表示・非表示を切り替えられるパスワード入力欄の作り方をまとめます。
要件
- 入力内容の表示・非表示を切り替えられる
- 表示・非表示の切り替え時に、キーボードが閉じない
定義元
- 入力内容の表示・非表示を切り替えられる
TextFieldとSecureFieldの切り替えによって実装します。
- 表示・非表示の切り替え時に、キーボードが閉じない
@FocusStateの切り替えによって実装します。
struct PasswordField: View {
let titleKey: LocalizedStringKey
@Binding var text: String
@State var isShowSecure = false
@FocusState var isTextFieldFocused: Bool
@FocusState var isSecureFieldFocused: Bool
init(_ titleKey: LocalizedStringKey, text: Binding<String>) {
self.titleKey = titleKey
_text = text
}
var body: some View {
HStack {
ZStack {
TextField(titleKey, text: $text)
.focused($isTextFieldFocused)
.keyboardType(.asciiCapable)
.autocorrectionDisabled(true)
.textInputAutocapitalization(.none)
.opacity(isShowSecure ? 1 : 0)
SecureField(titleKey, text: $text)
.focused($isSecureFieldFocused)
.opacity(isShowSecure ? 0 : 1)
}
Button {
if isShowSecure {
isShowSecure = false
isSecureFieldFocused = true
} else {
isShowSecure = true
isTextFieldFocused = true
}
} label: {
Image(systemName: isShowSecure ? "eye" : "eye.slash")
}
.buttonStyle(.plain)
}
.padding()
.overlay(
RoundedRectangle(cornerRadius: 16)
.stroke(Color.primary, lineWidth: 1)
)
}
}
呼び出し元
PasswordField("PasswordField", text: $password)
表示(TextField) | 非表示(SecureField) |
---|---|
入力内容が自動で削除されないようにする
ここまで実装された方や、同じような実装方法でアプリを作った方は一度
- 入力内容の表示
-
ABC
と入力 - 入力内容の非表示
-
D
と入力
のような動作をしてみてください。
ABCD
としたいところ、D
となったのではないでしょうか?
これはSecureFieldの、
フォーカス後の最初の入力の場合、入力内容が上書きされる
という挙動が関係しています。
この動作を検知して、上書きしていないように振る舞わせてます。
struct PasswordField: View {
let titleKey: LocalizedStringKey
@Binding var text: String
@State var isShowSecure = false
@FocusState var isTextFieldFocused: Bool
@FocusState var isSecureFieldFocused: Bool
+ @State var isFirstEntryAfterToggle = false
init(_ titleKey: LocalizedStringKey, text: Binding<String>) {
self.titleKey = titleKey
_text = text
}
var body: some View {
HStack {
ZStack {
TextField(titleKey, text: $text)
.focused($isTextFieldFocused)
.keyboardType(.asciiCapable)
.autocorrectionDisabled(true)
.textInputAutocapitalization(.none)
.opacity(isShowSecure ? 1 : 0)
SecureField(titleKey, text: $text)
.focused($isSecureFieldFocused)
.opacity(isShowSecure ? 0 : 1)
}
Button {
if isShowSecure {
isShowSecure = false
isSecureFieldFocused = true
+ if !text.isEmpty {
+ isFirstEntryAfterToggle = true
+ }
} else {
isShowSecure = true
isTextFieldFocused = true
+ isFirstEntryAfterToggle = false
}
} label: {
Image(systemName: isShowSecure ? "eye" : "eye.slash")
}
.buttonStyle(.plain)
}
.padding()
.overlay(
RoundedRectangle(cornerRadius: 16)
.stroke(Color.primary, lineWidth: 1)
)
+ .onChange(of: text) { oldValue, newValue in
+ if newValue.count == 1 && isFirstEntryAfterToggle {
+ UINotificationFeedbackGenerator().notificationOccurred(.success)
+ text = oldValue + newValue
+ }
+ isFirstEntryAfterToggle = false
+ }
}
}
最適なパスワード入力欄について考える
1つ前の章で、入力内容が自動で削除されないようにする
方法を紹介しましたが、なぜそのような問題が起きるのでしょうか?
そもそも、最適なパスワード入力欄とはどのようなものなのでしょうか?
まずは、HIGを見ます。
必要に応じてセキュリティ保護されたテキスト入力フィールドを使う。機密データを必要とするアプリやゲームでは、ユーザの入力内容が表示されないフィールドを使用します(通常、入力文字の代わりに小さい「●」が表示される)。デベロッパ向けのガイダンスは、SecureFieldを参照してください。
パスワードの入力には、SecureFieldを使用することが推奨されています。
HIGにはSecureFieldの詳しい効果は書かれていませんが、
SecureFieldには、
- サードパーティーキーボードのブロック
- コピーの禁止
- スクリーンショット、画面収録時の情報の保護
等のセキュリティ保護機能が備わっているため、SecureFieldを推奨していると考えています。
パスワードフィールドの内容は自動入力しないようにする。パスワードの入力または生体認証やキーチェーン認証の使用は必ずユーザに要求するようにしてください。
少し解釈は異なりますが、今回紹介したパスワード入力欄は、SecureFieldから見ると自動入力された判定になってしまっています。
- 入力内容の表示
- ABCと入力(TextFieldに値を入力している)
- 入力内容の非表示(SecureFieldにTextFieldの値を自動入力している)
このルールが原因で、入力内容が自動で削除される問題が起きていると考えています。
これらを元に、最適なパスワード入力欄を考えてみました。
要件
- セキュリティが保護されている
- 入力間違いのリスクを減少できる
- OSやバージョンの差がなく使用できる
案1: SecureFieldで2度パスワードを打ってもらう
SecureFieldを2つ実装し、2つの入力内容が同じ時に次に進めるようにします。
これが一番安全な方法ですが、入力内容の確認ができないデメリットがあります。
案2: 入力内容の表示中は編集できないようにする
今までの入力内容の表示・非表示は、TextFieldとSecureFieldの切り替えによって実装していましたが、TextとSecureFieldの切り替えによって実装するように変更します。
入力内容の確認ができますが、入力内容が画面収録やスクリーンショットに映ります。
表示(Text) | 非表示(SecureField) |
---|---|
宣伝
株式会社アルクでは、ディズニー ファンタスピークの開発をしています。
ディズニー ファンタスピークはディズニーの作品や音楽を楽しみながら、英語学習ができるアプリです。
英語を勉強したいけど、教科書みたいなのはちょっと…という方におすすめです。
Discussion
ここの箇所
と思うのですが、いかがでしょうか 🙇
ありがとうございます!
修正しました🙏