UITableView上で、キーボードを閉じる処理

#はじめに
キーボードを閉じる処理は、UIView上だと`UITapGestureRecognizer`で閉じる処理を追加すれば済みます。
しかし、UITableViewでは、
本来のタップイベントが殺される為、そのままでは弊害があります。

解決手段を2つ、紹介します。

#手段1：iOS7以降の標準機能を用いる
iOS7より、`UIScrollView`に`keyboardDismissMode`プロパティが追加されました。
`keyboardDismissMode`を設定するだけで事足ります。

```objc:sample
// 上下のスワイプで、キーボードを閉じる
scrollView.keyboardDismissMode = UIScrollViewKeyboardDismissModeOnDrag;

// 指でキーボードを下方に引っ込めるジェスチャーを追加
scrollView.keyboardDismissMode = UIScrollViewKeyboardDismissModeInteractive;
```
ストーリーボードの`keyboard`の項目から設定することも可能です。

UIScrollViewのプロパティなので、継承クラスのUITableViewでも利用できます。

#手段2：タップジェスチャーを制御する
手段1では、キーボードを閉じる動作はスワイプのみで、タップ動作はありません。
加えて、Viewのタッチイベントも有効なので、
「キーボード入力中なのに誤って画面をタップしてしまう」など、使い勝手の悪いケースが出てきます。

そこで、なんとかタップジェスチャーを使えるようにします。
以下、目的別にコード分けて記述します。

###タップでキーボードを閉じる
```objc:SampleViewController.m
-(void)viewDidAppear:(BOOL)animated
    // ジェスチャー追加：画面タップでキーボードを閉じる
    UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(closeKeyboard)];
    [self.view addGestureRecognizer:tapGesture];
    // 後述のイベント切り分け用
    tapGesture.delegate = self;
}

// キーボードを閉じる
-(void)closeKeyboard
{
    [[self view] endEditing:YES];
}
```
しかし、冒頭で述べた様に、
元からタップイベントが存在するUITableViewなどは、ジェスチャーに割り込まれ、元のタップイベントが発生しなくなります。

###タップジェスチャーとViewタップイベントの切り分け
Viewのタップイベントが殺されない様
・キーボード表示時は、タップジェスチャー
・キーボード非表示時は、View本来のタップイベント
それぞれが処理されるように切り分けを行います。

```objc:SampleViewController.h
// UIGestureRecognizerDelegateプロトコルの宣言
@interface SampleViewController : UITableViewController <UIGestureRecognizerDelegate>

```
```objc:SampleViewController.m
// タップジェスチャーの実行可否
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
    // キーボード表示中のみ、タップジェスチャーを実行する
    // 表示中でないなら、SubViewにイベントを移譲
    // （keyboardShowingは仮のフラグです）
    if (keyboardShowing) {
        return YES;
    } else {
        return NO;
    }
}
```
`gestureRecognizer:shouldReceiveTouch:`は、
戻り値にNOを返す事で、ジェスチャー処理をキャンセルできます。
その場合、View（ジェスチャーの下層レイヤ）に、ジェスチャーイベントが移ります。


###キーボード表示有無フラグの管理
タップイベントの切り分けを、キーボード表示有無で判断したいのですが、
キーボード表示状態を知るフラグが見当たりません。

仕方ないので、キーボード表示有無フラグを自前で用意します。

```objc:SampleViewController.m
@implementation SampleViewController {
    BOOL keyboardShowing;   // キーボード表示有無フラグ
}

// 画面表示後処理
-(void)viewDidAppear:(BOOL)animated
{
    // 通知センターの設定
    NSNotificationCenter *notification = [NSNotificationCenter defaultCenter];
    // 通知センターにキーボード表示処理開始を通知
    [notification addObserver:self
                     selector:@selector(keyboardWillShow)
                         name:UIKeyboardWillShowNotification
                       object:nil];
    // 通知センターにキーボード非表示処理終了を通知
    [notification addObserver:self
                     selector:@selector(keyboardDidHide)
                         name:UIKeyboardDidHideNotification
                       object:nil];
}

// 画面非表示後処理
-(void)viewDidDisappear:(BOOL)animated
{
    // 通知センターの解除（解除必須、忘れないこと）
    NSNotificationCenter *notification = [NSNotificationCenter defaultCenter];
    [notification removeObserver:self];
}

// イベント：キーボード表示開始
-(void)keyboardWillShow
{
    keyboardShowing = YES;
}

// イベント：キーボード表示終了
-(void)keyboardDidHide
{
    keyboardShowing = NO;
}
```
以上、
これでキーボード表示中は`keyboardShowing`がYESになり、
タップジェスチャーは、キーボード表示中のみ有効になります。

#最後に
手段2はタップ以外のジェスチャーでも可能です。
また、手段1・手段2は両立も可能です。

#参考
[[iOS 7] UIScrollView キーボードの扱い方が変わりました！](http://dev.classmethod.jp/references/ios-keyboarddismissmode/)