特定のViewのみUIGestureRecognizerを無効にする

# やりたいこと

親Viewにgestureをつけているけど、子Viewにはそのgestureに反応してほしくない！という時がありました。

こんな時にどう実装すればいいのかのメモを書こうと思います。
Swiftは2.1, Xcodeは7.1.1で書きました。

# 完成品

初期状態
![スクリーンショット 2015-11-14 18.38.47.png](https://qiita-image-store.s3.amazonaws.com/0/100736/d6a66bbc-0cad-b855-7b44-7945a1179d54.png)
↓ボタンをタップすると…
![スクリーンショット 2015-11-14 18.41.32.png](https://qiita-image-store.s3.amazonaws.com/0/100736/d23a9d91-3612-cf16-a349-4c135aa2aa6c.png)
こんなかんじの表示になります。
親View(薄暗くなっているview)をタップされた場合は初期状態に戻し、子View(真ん中の白いview)がタップされた場合は特に何もしない、というものを作ります。

# 実践

## つまづきポイント

何も考えずにこんなコードを書くと期待はずれの動きをします。


```swift:ViewController.swift
class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        self.setupButton()
    }
    
    private func setupButton()
    {
        let button = UIButton()
        button.frame = CGRectMake(0, 0, 100, 50)
        button.layer.position = CGPoint(x: self.view.bounds.width / 2, y: 300)
        button.backgroundColor = UIColor.blueColor()
        button.setTitle("view表示", forState: .Normal)
        button.addTarget(self, action: "tappedButton", forControlEvents: .TouchUpInside)
        self.view.addSubview(button)
    }
    
    @objc private func tappedButton()
    {
        let view = OverlayView(frame: self.view.frame)
        self.view.addSubview(view)
    }
```

```swift:OverlayView.swift
class OverlayView : UIView, UIGestureRecognizerDelegate {
    
    var whiteView:UIView!
    var tapGesture:UITapGestureRecognizer!
    
    internal required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override init(frame: CGRect)
    {
        super.init(frame: frame)
        self.backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.7)

        tapGesture = UITapGestureRecognizer(target: self, action: "tappedOverlayView")
        self.addGestureRecognizer(tapGesture)
        tapGesture.cancelsTouchesInView = false
        tapGesture.delegate = self

        self.addView()
    }
    
    private func addView()
    {
        whiteView = UIView(frame: CGRectMake(0, 0, 200, 200))
        whiteView.center = self.center
        whiteView.backgroundColor = UIColor.whiteColor()
        self.addSubview(whiteView)
    }
    
    @objc private func tappedOverlayView()
    {
        self.removeFromSuperview()
    }
    
    func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool
    {
        if (touch.view!.isDescendantOfView(whiteView)) {
            return false
        }
        return true
    }
}
```

子viewをタップしてもgestureが反応してしまいます。

## 解決策

OverlayView.swiftに3箇所修正を加えます。

1.UIGestureRecognizerDelegateを追加します。
2.initに以下のコードを足します。

```swift
tapGesture.cancelsTouchesInView = false
tapGesture.delegate = self
```
3.以下のメソッドを追加します。

```swift
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool
    {
        if (touch.view.isDescendantOfView(whiteView)) {
            return false
        }
        return true
    }
```

このメソッドで、gestureを反応させるべきかどうかを決めます。
if文中のtouch.view.isDescendantOfViewでタップしたviewが引数のviewと同じ、もしくはそのsubViewであるかどうかを判定します。
今回はwhiteViewがタップされても反応しないようにして欲しいので、その場合はfalseをreturnするようにします。

これで期待通りの動きをするコードになりました！

## 注意点

解決策の3で示したメソッドが使えるのはiOS8.0以降なので対応OSに注意する必要があります。


# 参考記事

http://anton0825.hatenablog.com/entry/20120516/1338539185
http://ttr-hamasaki.hatenablog.com/entry/2014/01/29/035549