質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

Q&A

解決済

1回答

1199閲覧

サイズ指定していない(constraintのみ)オブジェクトにTapGestureRecognizerを実装したい

punimi

総合スコア3

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

0グッド

0クリップ

投稿2020/08/12 21:08

編集2020/08/12 21:11

サイズを指定しないとUIGestureRecognizerが機能しない問題を解決したいです。

プロフィール編集画面の実装において UIImageView や UIView に UITapGestureRecognizer を乗っけたのですが一部が機能せず、色々と問題を探したところサイズを指定していないオブジェクトのみに問題が発生することがわかりました。

今回問題が発生したオブジェクトは constraintで大きさを決めているのですが、オートレイアウトの問題で明確なサイズは指定したくない状況です。 (constraint + heightのみ指定 の場合でもダメでした)

オブジェクトの大きさが未確定なのが問題なのかと思い、オブジェクトをaddSubviewさせた後に UITapGestureRecognizer を実装したのですがそれでもダメでした。。。

オブジェクトのサイズを指定せずにconstraintのみでも UIGestureRecognizer を機能させる方法があれば教えていただきたいです。

よろしくお願いします。

private let backgroundImageView: UIImageView = { let iv = UIImageView() iv.contentMode = .scaleAspectFill iv.clipsToBounds = true iv.image = UIImage(named: "background") iv.isUserInteractionEnabled = true return iv }() private let mask1View: UIView = { let view = UIView() view.backgroundColor = UIColor(white: 0, alpha: 0.3) return view }() private let camera1Button: UIButton = { let button = UIButton(type: .system) button.setImage(UIImage(named: "camera")?.withRenderingMode(.alwaysTemplate), for: .normal) button.tintColor = .white button.setDimensions(width: 30, height: 30) button.addTarget(self, action: #selector(handleBackgroundChange), for: .touchUpInside) return button }() lazy var profileImageView: UIImageView = { let iv = UIImageView() iv.contentMode = .scaleAspectFill iv.clipsToBounds = true iv.setDimensions(width: 90, height: 90) iv.layer.cornerRadius = 45 iv.layer.borderWidth = 5 iv.layer.borderColor = UIColor.white.cgColor iv.isUserInteractionEnabled = true return iv }() private let mask2View: UIView = { let view = UIView() view.backgroundColor = UIColor(white: 0, alpha: 0.3) view.setDimensions(width: 80, height: 80) view.layer.cornerRadius = 40 return view }() private let camera2Button: UIButton = { let button = UIButton(type: .system) button.setImage(UIImage(named: "camera")?.withRenderingMode(.alwaysTemplate), for: .normal) button.tintColor = .white button.setDimensions(width: 30, height: 30) button.addTarget(self, action: #selector(handleSelectImage), for: .touchUpInside) return button }() private let underlineView: UIView = { let view = UIView() view.backgroundColor = .systemGroupedBackground return view }() // MARK: - Lifecycle init(user: User) { self.user = user super.init(frame: .zero) configureUI() configure() configureGestureRecgnizer() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } // MARK: - Selector @objc func handleSelectImage() { delegate?.selectImage() } @objc func handleBackgroundChange() { delegate?.selectBackgroundImage() } // MARK: - Helper func configureUI() { addSubview(backgroundImageView) backgroundImageView.anchor(top: topAnchor, left: leftAnchor, right: rightAnchor, height: 160) addSubview(mask1View) mask1View.anchor(top: backgroundImageView.topAnchor, left: backgroundImageView.leftAnchor, bottom: backgroundImageView.bottomAnchor, right: backgroundImageView.rightAnchor) addSubview(camera1Button) camera1Button.centerX(inView: backgroundImageView) camera1Button.centerY(inView: backgroundImageView) addSubview(profileImageView) profileImageView.anchor(top: backgroundImageView.bottomAnchor, left: leftAnchor, paddingTop: -35, paddingLeft: 20) addSubview(mask2View) mask2View.centerX(inView: profileImageView) mask2View.centerY(inView: profileImageView) // addSubview(camera2Button) camera2Button.centerX(inView: profileImageView) camera2Button.centerY(inView: profileImageView) addSubview(underlineView) underlineView.anchor(left: leftAnchor, bottom: bottomAnchor, right: rightAnchor, height: 1) } func configure() { profileImageView.sd_setImage(with: URL(string: user.profileImageUrl), completed: nil) } func configureGestureRecgnizer() { let backgroundTap = UITapGestureRecognizer(target: self, action: #selector(handleBackgroundChange)) let profileTap = UITapGestureRecognizer(target: self, action: #selector(handleSelectImage)) backgroundImageView.addGestureRecognizer(backgroundTap) mask1View.addGestureRecognizer(backgroundTap) profileImageView.addGestureRecognizer(profileTap) mask2View.addGestureRecognizer(profileTap) }

※ anchor()とsetDimensions()はこちらで拡張実装した変数です。

変数の定義内でaddGestureRecognizerをしてもダメでした。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

TsukubaDepot

2020/08/12 23:04

何が想定通りに実行できないのかよく把握できていないのですが、backgroundImageView と同じ拘束をmask1View にかけると、backgroundImageView に対してセットした GestureRecognizer が認識されない、ということなのでしょうか。したがって、現状では両方に GestureRecognizer をセットしている、ということなのでしょうか。
punimi

2020/08/13 04:06

編集画面の初期ではmaskViewとButtonを表示させており、一度新しい画像を選択するとこの2つのオブジェクトは非表示にさせるのでbackgroundImageViewにもGestureRecognizerを実装しています。 今回の問題は『オブジェクトのサイズを指定していないとconstraintのみのレイアウトではGestureRecognizerが機能してくれないこと』です。 そのため、オブジェクトの上下関係や配置の問題は関係ないです。 問題の解決に取り組んだ際にそれぞれのオブジェクトを1つ残しそれ以外のaddSubviewを消すということを繰り返した結果、サイズを指定していないオブジェクトのみGestureRecognizerが機能していないという気づきに達した次第です。
TsukubaDepot

2020/08/13 04:22

問題が『オブジェクトのサイズを指定していないとconstraintのみのレイアウトではGestureRecognizerが機能してくれないこと』だということは認識しています。 それを確認するために、上と左右、および高さ定数の拘束をかけたViewに GestureRecognizer を追加した場合でも、GestureRecognizerは正しく機能するようです(コードベースでもStoryBoardでも同じ)。上と左右、高さはSuperview の一定倍数のような場合でも機能しました。なので、定数としてサイズを設定する必要はないのではないかと思った次第です。 anchor(top:left:right:height:)の実装はどのようになっているかわかりませんが、こちらでは backgroundImageView.translatesAutoresizingMaskIntoConstraints = false backgroundImageView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true backgroundImageView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true backgroundImageView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true backgroundImageView.heightAnchor.constraint(equalToConstant: 160).isActive = true のような実装で試しましたが、GestureRecognizerで設定したメソッドはやはり呼び出されているようです。
punimi

2020/08/13 06:23

それなら別のところに問題があったんですね、、、 また可能性を探してきます。 ご協力いただきありがとうございました。 出直してきます!
TsukubaDepot

2020/08/13 06:26

実装が異なる可能性は十分にありますが、あとで時間がある時にこちらで試したサンプルを回答本文におきますので、参考にしていただければと思います。
guest

回答1

0

ベストアンサー

ご提示いただいたコードはカスタムクラスの可能性もありますが、とりあえずこのような感じで一部を反映させてみたところ、問題なくタップは検知可能でした。

mask1ViewbackgroundImageView の子 view にする方法もあるとおもいます。

Swift

1import UIKit 2 3class ViewController: UIViewController { 4 private let backgroundImageView: UIImageView = { 5 let iv = UIImageView() 6 iv.contentMode = .scaleAspectFill 7 iv.clipsToBounds = true 8 iv.backgroundColor = .orange 9 //iv.image = UIImage(named: "background") 10 11 iv.isUserInteractionEnabled = true 12 13 return iv 14 }() 15 16 private let mask1View: UIView = { 17 let view = UIView() 18 view.backgroundColor = UIColor(white: 0, alpha: 0.3) 19 20 return view 21 }() 22 23 override func viewDidLoad() { 24 super.viewDidLoad() 25 // Do any additional setup after loading the view. 26 27 configureUI() 28 configure() 29 configureGestureRecgnizer() 30 } 31 32 func configureUI() { 33 view.addSubview(backgroundImageView) 34 35 backgroundImageView.translatesAutoresizingMaskIntoConstraints = false 36 backgroundImageView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true 37 backgroundImageView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true 38 backgroundImageView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true 39 backgroundImageView.heightAnchor.constraint(equalToConstant: 160).isActive = true 40 41 42 view.addSubview(mask1View) 43 // backgroundImageView の子 View にすれば、backgroundImageView の GestureRecognizer でも検知可能 44 //backgroundImageView.addSubview(mask1View) 45 46 mask1View.translatesAutoresizingMaskIntoConstraints = false 47 mask1View.topAnchor.constraint(equalTo: backgroundImageView.topAnchor).isActive = true 48 mask1View.leftAnchor.constraint(equalTo: backgroundImageView.leftAnchor).isActive = true 49 mask1View.rightAnchor.constraint(equalTo: backgroundImageView.rightAnchor).isActive = true 50 mask1View.bottomAnchor.constraint(equalTo: backgroundImageView.bottomAnchor).isActive = true 51 } 52 53 func configure() { 54 } 55 56 func configureGestureRecgnizer() { 57 let backgroundTap = UITapGestureRecognizer(target: self, action: #selector(handleBackgroundChange)) 58 backgroundImageView.addGestureRecognizer(backgroundTap) 59 // backgroundImageView の子 View になっていれば、こちらは設定不要 60 mask1View.addGestureRecognizer(backgroundTap) 61 } 62 63 @objc func handleBackgroundChange(_ sender: UITapGestureRecognizer){ 64 print(sender.view) 65 } 66}

投稿2020/08/13 07:26

TsukubaDepot

総合スコア5086

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

punimi

2020/08/14 14:37

後日触ってみるとまったくコードを変えていないのに UIGestureRecognizer が効くようになっていました、、、 シミュレータだけでなく実機でも効かなかったのでXcodeの問題ではないと思っていたのですが、Xcodeのバグだったようです。 お手数おかけしました、、
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問