【SwiftUI】キーボードで文字が隠れないようにする制御

# キーボード処理
TextFieldを選択し、文字を入力するときにキーボードで隠れてしまい入力箇所が見れない場合があります。
キーボードの高さだけビュー全体を上にあげることで解決することが常套手段で、それをSwiftUIでも実装してみたのが今回の話です。
ObservableObjectのクラスで実装を行います。

# 実装
## キーボードの制御を管理するファイルを作ってください。

```swift:KeyboardObserver.swift
import SwiftUI
import Combine

class KeyboardObserver: ObservableObject {

  @Published var keyboardHeight: CGFloat = 0.0

  // キーボードの監視開始
  func addObserver() {
    NotificationCenter
      .default
      .addObserver(
		self,
		selector: #selector(self.keyboardWillChangeFrame),
		name: UIResponder.keyboardWillChangeFrameNotification,
		object: nil)
  }

  // キーボードの監視終了
  func removeObserver() {
    NotificationCenter
      .default
      .removeObserver(self,
                      name: UIResponder.keyboardWillChangeFrameNotification,
                      object: nil)
  }

  // キーボードのフレーム検知処理
  @objc
  func keyboardWillChangeFrame(_ notification: Notification) {
    if let endFrame = notification
      .userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue,
    let beginFrame = notification
      .userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue {
      let endFrameMinY: CGFloat = endFrame.cgRectValue.minY
      let beginFrameMinY: CGFloat = beginFrame.cgRectValue.minY

      self.keyboardHeight = beginFrameMinY - endFrameMinY
		if self.keyboardHeight < 0 {
        self.keyboardHeight = 0
      }
    }
  }
}

```

これで下準備は完了です。

## 利用例（全体適用）

```swift:Sample.swift

struct HogeView: View {
	
	@State var inputText = ""
    @ObservedObject var keyboard = KeyboardObserver()
	
    var body: some View {
		VStack {
			TextField("入力1", text: $inputText)
			.onAppear{
				self.keyboard.addObserver()
			}.onDisappear{
				self.keyboard.removeObserver()
			}.padding(.bottom, keyboard.keyboardHeight)
		}
    }
}
```

onAppearとonDisappearでは、Viewが表示されたときにキーボードの高さを監視していて、Viewが非表示になった段階で監視を終わらせています。
※シュミレーターで表示させないとキーボードの挙動を確認できません

## 利用例（個別適用）
```swift:Sample.swift

struct HogeView: View {
	
	@State var inputText = ""
	@State var inputText_2 = ""
	@State var inputText_3 = ""
	@ObservedObject var keyboard = KeyboardObserver()
	
    var body: some View {
		VStack {
			//1
			TextField("入力1", text: $inputText)
			//2
			TextField("入力2", text: $inputText_2)
			//3(ここに適用)
			TextField("入力3", text: $inputText_3)
		}
    }
}
```
上記コードの三つ目のTextFieldにのみキーボードの操作を適用させたい場合は以下のように記述します。

```swift:Sample.swift

struct HogeView: View {
	
	@State var inputText = ""
	@State var inputText_2 = ""
	@State var inputText_3 = ""
	@ObservedObject var keyboard = KeyboardObserver()
	
    var body: some View {
		VStack {
			//1
			TextField("入力1", text: $inputText)
			//2
			TextField("入力2", text: $inputText_2)
			//3（ここに適用）
			TextField("入力3", text: $inputText_3, onEditingChanged: {
				begin in
				
				if begin {
					self.keyboard.addObserver()
				} else {
					self.keyboard.removeObserver()
				}

			})
		}.padding(.bottom, keyboard.keyboardHeight)
    }
}
```

TextFieldのonEditingChangedでBoolで管理します。三つ目のTextFieldに対して入力操作が始まったときにキーボードの監視を開始し、入力が終わったら終了するようにしています。

# まとめ
備忘録的な要素が強く、抜けがあると思いますがご容赦ください。
TextFieldがいくつもある場合に、個別適用を用いることが多くなりそうです。

# 参考記事
- https://qiita.com/shira-shun/items/2768393fa3e0be333639