【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クラスが変わらずアプリの管理を司っているのですね。

まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。

ご覧いただきありがとうございました。

searchbox

スポンサー

ProFile

ame

趣味:読書,プログラミング学習,サイト制作,ブログ

IT嫌いを克服するためにITパスを取得しようと勉強してからサイト制作が趣味に変わりました笑
今はCMSを使わずこのサイトを完全自作でサイト運営中〜

New Article

index