I am currently facing an issue with UITapGestureRecognizer in my custom UIView class within an iOS app developed in Swift. Although I did set the gesture recognizer as per usual practices, it seems not responding to taps at all. Below is the relevant part of my code setup:
ViewController
class myViewController : ViewController {
init() {
super.init(nibName: nil, bundle: nil)
self.view.addSubview(self.myView)
self.myView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
self.myView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor).isActive = true
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
lazy var myView : MyCustomView = {
let view = MyCustomView(frame: CGRect(x: 0, y: 0, width: 476.0, height: 398.0))
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
}
Custom View
class MyCustomView : UIView {
override init(frame: CGRect) {
super.init(frame: frame)
self.widthAnchor.constraint(equalToConstant: frame.width).isActive = true
self.heightAnchor.constraint(equalToConstant: frame.height).isActive = true
self.mView.topAnchor.constraint(equalTo: self.topAnchor, constant: 10).isActive = true
self.mView.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -20).isActive = true
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
private lazy var mView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = UIColor.init(hexString: "#EDEEEF")
view.isUserInteractionEnabled = true
let gesture = UITapGestureRecognizer(target: self, action: #selector(tappedDropDown))
view.addGestureRecognizer(gesture)
return view
}()
@objc func tappedDropDown(sender : UITapGestureRecognizer){
print("Tapped")
}
}
This is also similar to this but I can't understand what is happening in my case. I have already checked to ensure isUserInteractionEnabled is set to true, and there are no views overlaying mView that could be intercepting the touches.
There are numerous issues with the example code that will stop it working, and some that will stop it even compiling (unterminated string literals, trying to set autolayout (AL) constraints without setting TAMIC to false, etc).
But I think* the issue stopping the tap gesture working is that the layout code is a mix of frame sizes and AL constraints and it isn't laying things out the way you believe it is, which means that the custom view's child view isn't receiving the gesture to respond too.
Changing the layout code so that it is all AL makes the tap gesture work without any changes to gesture recogniser or its action method.
I'm assuming the layout you're aiming for is the VC's view with a subview (your CustomView
) which in turn has its own subview (your mView
, which below I have renamed subView
to make it clearer), in a three-tiered pyramid type of stack.
I say 'think' above because I may have corrected some other error in fixing the layout code. I've only corrected the code necessary to get it to compile and give a working UI. I've not intentionally addressed any other issue as they are not relevant to the question.
The code below works in that it lays out the three views (the VC's view, the myView
(a CustomView
), and the CustomView's subview
and adds the tapGestureRecogniser
to subView
. The gestureRecogniser fires its selector method when tapped. It might not be laid out how you want as it was hard to tell the layout you were trying to achieve, but it does demonstrate that the gestureRecogniser works as expected.
class myViewController : UIViewController {
lazy var myView : MyCustomView = {MyCustomView(frame: .zero)}()
init() {
super.init(nibName: nil, bundle: nil)
view.heightAnchor.constraint(equalToConstant: 600).isActive = true
view.addSubview(myView)
myView.topAnchor.constraint(equalTo: view.topAnchor, constant: 50).isActive = true
myView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -50).isActive = true
myView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -50).isActive = true
myView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 50).isActive = true
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
class MyCustomView : UIView {
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .systemOrange
translatesAutoresizingMaskIntoConstraints = false
addSubview(subView)
subView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: -50).isActive = true
subView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -50).isActive = true
subView.topAnchor.constraint(equalTo: topAnchor, constant: 50).isActive = true
subView.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -50).isActive = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private lazy var subView: UIView = {
let view = UIView()
view.backgroundColor = .systemBlue
view.translatesAutoresizingMaskIntoConstraints = false
view.isUserInteractionEnabled = true
let gesture = UITapGestureRecognizer(target: self, action: #selector(tappedDropDown))
view.addGestureRecognizer(gesture)
return view
}()
@objc func tappedDropDown(sender : UITapGestureRecognizer){
print("Tapped")
}
}