SwiftUIの複数なテキストフィールドのキーボードを消す方法

2022/03/15に公開


例えば画面には一個のテキストフィールドだけが存在する場合、
キーボードを消すために、その消す用の@FocusState変数を作り、フォーカス状態と連携させ実装することができます。

しかし、複数なテキストフィールドが存在する画面には、同じ多く@FocusState変数を作るのが、管理しにくいと思います。

そういう悩みを解決するために、以下のフォーカス管理コードを導入しましょう。

初めに

まずはサンプルコードはこうです。
「name」と「description」の二つのテキストフィールドがあります。

struct TestView: View {
    @State private var name = ""
    @State private var description = ""
    
    var body: some View {
        List {
            TextField("name", text: $name)
            TextField("description", text: $description)
        }
    }
}

カスタムenumタイプを定義

テキストフィールドの数と名前を合わせて、enumを作ります

struct TestView: View {
    @State private var name = ""
    @State private var description = ""
    
+   private enum Field: Hashable {
+       case name, description
+   }
    
    var body: some View {
        List {
            TextField("name", text: $name)
            TextField("description", text: $description)
        }
    }
}

@FocusState変数を定義

@FocusState変数は、現在フォーカス中か、フォーカスしてないのか(Bool型)の定義ではなく
現在フォーカスしているのがどのテキストフィールドか(カスタムField型)を定義します。

テキストフィールドが一つもフォーカスしてない状態も可能なので
(キーボードが閉じている画面)
Field?で型を定義します

struct TestView: View {
    @State private var name = ""
    @State private var description = ""
+   @FocusState private var focusedField: Field?
    
    private enum Field: Hashable {
        case name, description
    }
    
    var body: some View {
        List {
            TextField("name", text: $name)
            TextField("description", text: $description)
        }
    }
}

Modifier実装

テキストフィールドと、先ほど定義した@FocusState変数を連携させます

struct TestView: View {
    @State private var name = ""
    @State private var description = ""
    @FocusState private var focusedField: Field?
    
    private enum Field: Hashable {
        case name, description
    }
    
    var body: some View {
        List {
            TextField("name", text: $name)
+               .focused($focusedField, equals: .name)
            TextField("description", text: $description)
+               .focused($focusedField, equals: .description)
        }
    }
}

キーボードを閉じるボタン

キーボードの上に、「Done」ボタンを加えて
そのボタンをクリックすると、フォーカス中のボタンはどっちかの変数にnilを定義させ
キーボードを閉じます。

struct TestView: View {
    @State private var name = ""
    @State private var description = ""
    @FocusState private var focusedField: Field?
    
    private enum Field: Hashable {
        case name, description
    }
    
    var body: some View {
        List {
            TextField("name", text: $name)
                .focused($focusedField, equals: .name)
            TextField("description", text: $description)
                .focused($focusedField, equals: .description)
        }
+       .toolbar {
+           ToolbarItem(placement: .keyboard) {
+               HStack {
+                   Spacer()
+                   Button("Done") {
+                       focusedField = nil
+                   }
+               }
+           }
+       }
    }
}

完了

Discussion