SwiftUIでキーボードを閉じる

## SwiftUIでキーボードを閉じたい!
SwiftUIでは、キーボード自体に閉じるボタンを追加することはできません。しかし、画面のどこかをタップするとキーボードが閉じるようにすることは可能です。

以下のように、TapGestureを使用して画面全体にタップジェスチャーを追加し、そのアクションでpinFocusStateをnilに設定します。
```swift
@FocusState var focus:Bool

var body: some View {
    VStack {
        // Your existing code...
    }
    .gesture(
        TapGesture()
            .onEnded { _ in
                pinFocusState = nil
            }
    )
}
```

こちらに完成品のコードございます。pincodeなるものを作りたくて試してました。キーボードが閉じれなくて困っておりました💦
キーボードの上の画面をタップすると閉じます!

状態を扱うコード
```swift
import SwiftUI
import Combine

struct OtpModifer: ViewModifier {

    @Binding var pin : String

    var textLimt = 1

    func limitText(_ upper : Int) {
        if pin.count > upper {
            self.pin = String(pin.prefix(upper))
        }
    }


    //MARK -> BODY
    func body(content: Content) -> some View {
        content
            .multilineTextAlignment(.center)
            .keyboardType(.numberPad)
            .onReceive(Just(pin)) {_ in limitText(textLimt)}
            .frame(width: 45, height: 45)
            .background(Color.white.cornerRadius(5))
            .background(
                RoundedRectangle(cornerRadius: 5)
                    .stroke(Color("blueColor"), lineWidth: 2)
            )
    }
}
```

Viewのコード
```swift
import SwiftUI
import Combine

struct OtpFormFieldView: View {
    //MARK -> PROPERTIES

    enum FocusPin {
        case  pinOne, pinTwo, pinThree, pinFour
    }

    @FocusState private var pinFocusState : FocusPin?
    @State var pinOne: String = ""
    @State var pinTwo: String = ""
    @State var pinThree: String = ""
    @State var pinFour: String = ""
    
    @FocusState var focus:Bool


    //MARK -> BODY
    var body: some View {
        VStack {
            
            Text("Verify your Email Address")
                .font(.title2)
                .fontWeight(.semibold)
            
            
            Text("Enter 4 digit code we'll text you on Email")
                .font(.caption)
                .fontWeight(.thin)
                .padding(.top)
            
            HStack(spacing:15, content: {
                
                TextField("", text: $pinOne)
                    .modifier(OtpModifer(pin:$pinOne))
                    .onChange(of:pinOne){newVal in
                        if (newVal.count == 1) {
                            pinFocusState = .pinTwo
                        }
                    }
                    .focused($pinFocusState, equals: .pinOne)
                    .overlay(
                        RoundedRectangle(cornerRadius: 3)
                            .stroke(Color.blue, lineWidth: 2)
                    )
                
                TextField("", text:  $pinTwo)
                    .modifier(OtpModifer(pin:$pinTwo))
                    .onChange(of:pinTwo){newVal in
                        if (newVal.count == 1) {
                            pinFocusState = .pinThree
                        }
                    }
                    .focused($pinFocusState, equals: .pinTwo)
                    .overlay(
                        RoundedRectangle(cornerRadius: 3)
                            .stroke(Color.blue, lineWidth: 2)
                    )
                
                
                TextField("", text:$pinThree)
                    .modifier(OtpModifer(pin:$pinThree))
                    .onChange(of:pinThree){newVal in
                        if (newVal.count == 1) {
                            pinFocusState = .pinFour
                        }
                    }
                    .focused($pinFocusState, equals: .pinThree)
                    .overlay(
                        RoundedRectangle(cornerRadius: 3)
                            .stroke(Color.blue, lineWidth: 2)
                    )
                
                
                TextField("", text:$pinFour)
                    .modifier(OtpModifer(pin:$pinFour))
                    .focused($pinFocusState, equals: .pinFour)
                    .overlay(
                        RoundedRectangle(cornerRadius: 3)
                            .stroke(Color.blue, lineWidth: 2)
                    )
                
                
            })
            .padding(.vertical)
            
            
            Button(action: {}, label: {
                Spacer()
                Text("Veify")
                    .font(.system(.title3, design: .rounded))
                    .fontWeight(.semibold)
                    .foregroundColor(.white)
                Spacer()
            })
            .padding(15)
            .background(Color.blue)
            .clipShape(Capsule())
            .padding()
        }
        .gesture(
        TapGesture()
            .onEnded { _ in
                pinFocusState = nil
            }
    )
    }
}

struct OtpFormFieldView_Previews: PreviewProvider {
    static var previews: some View {
        OtpFormFieldView()
    }
}
```

Previewするとこんな感じです。
![スクリーンショット 2024-03-05 21.09.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2303722/8371c198-01d4-bc3b-59f8-e6b3632ab528.png)

作ったコンポーネントをimportしてビルドして試してみてください。
```swift
import SwiftUI

struct ContentView: View {
    
    var body: some View {
        OtpFormFieldView()
    }
    
    struct ContentView_Previews: PreviewProvider {
        static var previews: some View {
            ContentView()
        }
    }
}
```

## 最後に
海外の記事を参考に、pincodeのようなUIとキーボードを閉じるロジックを実装する方法をご紹介いたしました。番号を入力するキーボードだと閉じるのに工夫が必要でした。