Search code examples
iosswiftuiviewuigesturerecognizeruitapgesturerecognizer

UITapGestureRecognizer Not Triggering in Custom UIView in iOS


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.


Solution

  • 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")
       }
    }