【Swift】UITextField, UITextViewがキーボードで隠れるのを防ぐ(UIScrollViewを使わずに)

## はじめに
`UITextField`を画面に配置したはいいけど、下の写真のようにキーボードが出てくると隠れるというのはよくあることだと思います。
UIScrollViewを使ったりしている方法がありますが、画面にUIScrollViewを配置したりするのは面倒なので、画面をそもそもずらす方法を見つけたので、それをXcode8, Swift3で実装しました。

![アートボード 1.png](https://qiita-image-store.s3.amazonaws.com/0/75302/ec77907a-c422-cc23-cc97-6c62b5edb8f8.png)

## コード
### 実装前

```swift:ViewController.swift
import UIKit

class ViewController: UIViewController, UITextFieldDelegate {
    
    @IBOutlet var firstTextField: UITextField! {
        
        didSet {
            
            firstTextField.delegate = self
        }
    }
    
    
    @IBOutlet var secondTextField: UITextField! {
        
        didSet {
            
            secondTextField.delegate = self
        }
    }

    override func viewDidLoad() {
        
        super.viewDidLoad()
    }
    
    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        
        textField.resignFirstResponder()
        return true
    }
}
```

これだと、キーボードがReturnキーを押したときに下がるだけの実装です。
ここから、`NotificationCenter`を使って、キーボードが表示されたり消えたりすることを検知します。
そして、キーボードが表示されたり消えたときに、`animate(withDuration duration: TimeInterval, animations: @escaping () -> Swift.Void)`を使って、画面全体をずらします。


## 実装
まず`Notification`を設定するメソッドと削除するメソッドを用意し、それぞれ`viewWillAppear(_ animated: Bool)`と`viewWillDisappear(_ animated: Bool)`で呼び出します。
それぞれのメソッドは以下のように設定します。

```swift:ViewController.swift
    // Notificationを設定
    func configureObserver() {
        
        let notification = NotificationCenter.default
        notification.addObserver(self, selector: #selector(keyboardWillShow(notification:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
        notification.addObserver(self, selector: #selector(keyboardWillHide(notification:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
    }
    
    // Notificationを削除
    func removeObserver() {
        
        let notification = NotificationCenter.default
        notification.removeObserver(self)
    }
```

`Notification`に設定した`selector`のメソッドを設定します。

```swift:ViewController.swift
    // キーボードが現れた時に、画面全体をずらす。
    func keyboardWillShow(notification: Notification?) {
        
        let rect = (notification?.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue
        let duration: TimeInterval? = notification?.userInfo?[UIKeyboardAnimationDurationUserInfoKey] as? Double
        UIView.animate(withDuration: duration!, animations: { () in
            let transform = CGAffineTransform(translationX: 0, y: -(rect?.size.height)!)
            self.view.transform = transform
            
        })
    }
    
    // キーボードが消えたときに、画面を戻す
    func keyboardWillHide(notification: Notification?) {
        
        let duration: TimeInterval? = notification?.userInfo?[UIKeyboardAnimationCurveUserInfoKey] as? Double
        UIView.animate(withDuration: duration!, animations: { () in
            
            self.view.transform = CGAffineTransform.identity
        })
    }
```

以上で、完了です。

以下にすべてのコードを載せておきます。

## 完成品

```swift:ViewController.swift

import UIKit

class ViewController: UIViewController, UITextFieldDelegate {
    
    @IBOutlet var firstTextField: UITextField! {
        
        didSet {
            
            firstTextField.delegate = self // デリゲートをセット
        }
    }
    
    
    @IBOutlet var secondTextField: UITextField! {
        
        didSet {
            
            secondTextField.delegate = self // デリゲートをセット
        }
    }

    override func viewDidLoad() {
        
        super.viewDidLoad()
    }
    
    override func viewWillAppear(_ animated: Bool) {
        
        super.viewWillAppear(animated)
        self.configureObserver()
        
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        
        super.viewWillDisappear(animated)
        self.removeObserver() // Notificationを画面が消えるときに削除
    }
    
    // Notificationを設定
    func configureObserver() {
        
        let notification = NotificationCenter.default
        notification.addObserver(self, selector: #selector(keyboardWillShow(notification:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
        notification.addObserver(self, selector: #selector(keyboardWillHide(notification:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
    }
    
    // Notificationを削除
    func removeObserver() {
        
        let notification = NotificationCenter.default
        notification.removeObserver(self)
    }
    
    // キーボードが現れた時に、画面全体をずらす。
    func keyboardWillShow(notification: Notification?) {
        
        let rect = (notification?.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue
        let duration: TimeInterval? = notification?.userInfo?[UIKeyboardAnimationDurationUserInfoKey] as? Double
        UIView.animate(withDuration: duration!, animations: { () in
            let transform = CGAffineTransform(translationX: 0, y: -(rect?.size.height)!)
            self.view.transform = transform
            
        })
    }
    
    // キーボードが消えたときに、画面を戻す
    func keyboardWillHide(notification: Notification?) {
        
        let duration: TimeInterval? = notification?.userInfo?[UIKeyboardAnimationCurveUserInfoKey] as? Double
        UIView.animate(withDuration: duration!, animations: { () in
            
            self.view.transform = CGAffineTransform.identity
        })
    }
    
    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        
        textField.resignFirstResponder() // Returnキーを押したときにキーボードを下げる
        return true
    }
}


```

## 参考URL

[TextViewをキーボードに隠されず、高さが動的に変わるようにする](http://hacknote.jp/archives/7958/)


## サンプルコード

https://github.com/ShinokiRyosei/Keyboard-Sampler