ある日のこと……
画像の上にボタンを置いて、ボタンタップ時に処理を追加しようとした。
作りたいもの
完成図
早速作ってみる
コードを書いた
これで完了!……と思うじゃん?
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
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
当然ですが、isUserInteractionEnabled
に関する項目はない。
うーん……🤔
DeveloperサイトをisUserInteractionEnabled
でググってみた
そういえば、UIButtonのsuperViewをUIImageViewにしたぞ……?
superViewをUIViewに変更して再度ビルドして試してみる。
- imageView.addSubview(button)
+ view.addSubview(button)
今度はちゃんとタップが検出され、コンソールにbutton tapped!!
と表示された!
何故ボタンのタップが検出されなかったか
理由が明記された文章は探した限りだと見つかりませんでした。
ただ、UIImageViewのsubViewだったUIButtonのタップが検出されなかったのに、UIViewのsubviewであったUIButtonのタップが検出された事実を考えれば、あるviewのisUserInteractionEnabled
がfalseの場合、そのsubviewのisUserInteractionEnabled
がtrueであってもタップは検出されなくなってしまうようです。
解決策
画像の上にボタンを置きたくなることはあると思います。その場合、簡単な対処法としては
- UIImageViewの
isUserInteractionEnabled
をtrueに変更する - UIButtonのsuperViewをUIImageViewではないものにする
の2つが考えられます。どちらを選択するかは、Viewの構造によって適切に選択すると良いと思います。
上記の場合は、viewをsuperviewにする後者よりは、UIImageViewをsuperviewとする前者の方がよい気がします。
ちなみに今回私がハマったのは、UICollectionViewCell上に置いたUIImageViewとその上のUIButtonだったため、後者を選択しCellをsuperviewとしました。
Debug View Hierarchyだとどうしてもbuttonが最も手前に見えるので一瞬しっくりこないのですが、まあsubviewなのだからそういうものなのかもしれませんね🤔