【SwiftUI】@FocusStateとは?複数の入力フォームのフォーカスコントロール
この記事からわかること
- SwiftUIの@FocusStateの使い方
- フォーカスを制御、操作する方法
- 複数の入力フォームの紐付け
- フォーカスを解除する方法
index
[open]
\ アプリをリリースしました /
友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-
posted withアプリーチ
参考文献:公式リファレンス:@FocusState
SwiftUIの入力フォームなどのフォーカスをコントロールできる@FocusState
についてまとめていきたいと思います。
@FocusStateとは?
@frozen @propertyWrapper struct FocusState<Value> where Value : Hashable
@FocusState
とはiOS15以降(Swift5.1)から使えるようになったProperty Wrapper(プロパティラッパ)の1つです。プロパティラッパはプロパティに対する操作や処理をカプセル化して定義することで特定のキーワードを付与するだけで任意の動作や挙動を行わせることができる便利な機能です。
@FocusState
は画面内に設置されている入力フォームに対してのフォーカスをコントロールできる機能を提供しています。これにより、ユーザーがボタンを操作をしなくてもフォーカスを当てたり、また任意の処理の後にフォーカスを外すといったユーザーがスムーズにアプリを操作するための機能を実装することが可能になっています。
例えばTextFieldなどはフォーカスが当たることでキーボードが出現しますが、キーボードが出現中にフォーカスを解除することでキーボードをユーザー操作なく閉じることも可能になります。
フォーカス制御の仕組み
@FocusState
はプロパティに対して付与するプロパティラッパですが、対象のプロパティはBool型
もしくはHashble
型である必要があります。
@FocusState var isActive:Bool
このプロパティに格納されている値に応じてフォーカスが当たっているか外れているかを識別しコントロールされるようになります。そのため初期値は格納できません。
対象のビューに対してはfocused
モディファイアを使用して@FocusState
を付与したプロパティと紐づけることでビューに対してフォーカスを制御(紐付ける)ことができます。
TextField("name", text: $name)
.focused($isActive)
Bool
型の場合はフォーカスが当たった場合にtrue
が、外れた場合にfalse
へと自動で更新されます。
コードからフォーカスを操作する
@FocusState
を付与したプロパティに対してtrue
またはfalse
を格納することでフォーカスをコードから操作することが可能です。
Button {
isActive.toggle()
} label: {
Text(isActive ? "OFF" : "ON")
}
複数入力フォームがある場合のフォーカス制御
入力フォームが複数ある場合は、@FocusState
を付与するプロパティHashble
型で定義します。公式リファレンスの「ログインフォームが未入力ならフォーカスを当てる」サンプルコードが分かりやすくて参考になります。
struct LoginFormView: View {
enum Field: Hashable {
case username
case password
}
@State private var username = ""
@State private var password = ""
@FocusState private var focusedField: Field?
var body: some View {
Form {
TextField("Username", text: $username)
.focused($focusedField, equals: .username)
SecureField("Password", text: $password)
.focused($focusedField, equals: .password)
Button("Sign In") {
if username.isEmpty {
focusedField = .username
} else if password.isEmpty {
focusedField = .password
} else {
// Login
}
}
}
}
}
Hashable
型で制御する場合はフォーカスが当たっている場合は列挙型で定義した値が、外れている場合はnil
が格納されるようになります。なので定義の際はnil
を許容できるようにオプショナル型にする必要があります。
@FocusState private var focusedField: Field?
Swift UIでのフォーカス解除方法
現在アクティブになっているフォーカスを解除するためにはプロパティに対してfalse
もしくはnil
を格納すればOKでした。
ですがこの方法でも「TextFieldをアプリ内から追加/削除できる機能」を実装していた際にうまく動作しませんでした。具体的にはフォーカスが当たっている状態で対象のTextFieldを削除しようとすると「index out of range」となりアプリが停止してしまいます。詳細は以下記事を参考にしてもらうとして解決方法としては以下のようにUIKit側で使用するUIApplication
クラスからフォーカスを解除することです。
UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
// 削除処理の前にフォーカスを解除する
memberArray.removeLast()
当然と言えば当然なのかもしれませんがSwift UIでもUIApplicationクラスが変わらずアプリの管理を司っているのですね。
まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。
ご覧いただきありがとうございました。