UIButtonをaddSubViewする時はsuperviewを適切に選択すべし（戒め）

# ある日のこと……
画像の上にボタンを置いて、ボタンタップ時に処理を追加しようとした。

## 作りたいもの
### 完成図
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149055/68cbb2d1-9656-6e05-ae52-4f18127da3b3.png" width="250px"/>

## 早速作ってみる
### コードを書いた
これで完了！……と思うじゃん？

```ViewController.swift
import UIKit

final class ViewController: UIViewController {
    private lazy var imageView: UIImageView = {
        let imageView = UIImageView()
        // 本当は画像を置いていたのだが、サンプル用のコードなのでbackgroundColorで妥協
        imageView.backgroundColor = UIColor.systemPink.withAlphaComponent(0.5)
        imageView.layer.cornerRadius = 16
        imageView.clipsToBounds = true
        imageView.translatesAutoresizingMaskIntoConstraints = false
        return imageView
    }()

    private lazy var button: UIButton = {
        let button = UIButton()
        button.setTitle("Please tap!!", for: .normal)
        button.setTitleColor(.blue, for: .normal)
        button.titleLabel?.font = .systemFont(ofSize: 24)
        button.translatesAutoresizingMaskIntoConstraints = false
        button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
        return button
    }()

    private func layoutImageView() {
        view.addSubview(imageView)
        NSLayoutConstraint.activate([
            imageView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            imageView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
            imageView.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.8),
            imageView.heightAnchor.constraint(equalTo: view.widthAnchor, multiplier: 9 / 16),
        ])
    }

    private func layoutButton() {
        imageView.addSubview(button)
        NSLayoutConstraint.activate([
            button.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            button.centerYAnchor.constraint(equalTo: view.centerYAnchor),
        ])
    }

    @objc
    private func buttonTapped() {
        print("button tapped!!")
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        layoutImageView()
        layoutButton()
    }
}
```

### しかし…
`⌘+R`を押してビルドされるのを待ち、いざ、ボタンをタップ！！
……あれ？コンソールに何も出てこないぞ？？どうやらボタンがタップできていないらしい。
何が悪いんだ？

## 原因究明
### UIViewには、`isUserInteractionEnabled`というプロパティがある
`isUserInteractionEnabled`はタップなどのイベントを有効にするかどうかのプロパティである。
で、このデフォルト値はtrueであったはず。なんたって、特にUIButtonはタップイベントを検知するためのUIパーツだし。

### 念のため、ドキュメントを確認しよう
[isUserInteractionEnabled - UIView | Apple Developer Documentation](https://developer.apple.com/documentation/uikit/uiview/1622577-isuserinteractionenabled)

> The default value of this property is true.

ですよねー。
……と思っていたら、下の項目にこんなことが。

> Some UIKit subclasses override this property and return a different default value. See the documentation for that class to determine if it returns a different value.

なるほど。UIKitのサブクラスの中にはデフォルト値が`true`ではないものがあると。

念のため、UIButtonのドキュメントも見てみましょう。
[UIButton - UIKit | Apple Developer Documentation](https://developer.apple.com/documentation/uikit/uibutton)
当然ですが、`isUserInteractionEnabled`に関する項目はない。
うーん……🤔

### Developerサイトを`isUserInteractionEnabled`でググってみた
<img width="882" alt="スクリーンショット 2019-12-09 0.06.53.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149055/f0aaa924-0f12-6714-7e5a-d95f2eb1ce73.png">

そういえば、UIButtonのsuperViewをUIImageViewにしたぞ……？
superViewをUIViewに変更して再度ビルドして試してみる。

```diff
-        imageView.addSubview(button)
+        view.addSubview(button)
```

今度はちゃんとタップが検出され、コンソールに`button tapped!!`と表示された！

## 何故ボタンのタップが検出されなかったか
理由が明記された文章は探した限りだと見つかりませんでした。
ただ、UIImageViewのsubViewだったUIButtonのタップが検出されなかったのに、UIViewのsubviewであったUIButtonのタップが検出された事実を考えれば、あるviewの`isUserInteractionEnabled`がfalseの場合、そのsubviewの`isUserInteractionEnabled`がtrueであってもタップは検出されなくなってしまうようです。

### 解決策
画像の上にボタンを置きたくなることはあると思います。その場合、簡単な対処法としては

1. UIImageViewの`isUserInteractionEnabled`をtrueに変更する
2. UIButtonのsuperViewをUIImageViewではないものにする

の2つが考えられます。どちらを選択するかは、Viewの構造によって適切に選択すると良いと思います。
上記の場合は、viewをsuperviewにする後者よりは、UIImageViewをsuperviewとする前者の方がよい気がします。
ちなみに今回私がハマったのは、UICollectionViewCell上に置いたUIImageViewとその上のUIButtonだったため、後者を選択しCellをsuperviewとしました。


Debug View Hierarchyだとどうしてもbuttonが最も手前に見えるので一瞬しっくりこないのですが、まあsubviewなのだからそういうものなのかもしれませんね🤔