Prevent button to trigger when it's behind a view with UITapGestureRecognizer

Viewed 590

I have the following setup :

enter image description here

  1. Blue background at zPosition = 0
  2. Yellow button at zPosition = 0 with an action to print "Button tapped"
  3. UIView with grey background and 0.8 alpha at zPosition = 1 with a UITapGestureRecognizer with an action to print "Grey view tapped"

When I tapped at the grey area, "Grey view tapped" is printed. But if I tapped at the location of the yellow button, "Button tapped" is printed. I expected to always have printed "Grey view tapped" because that view is in front of the button. How can I prevent the button that is behind a view to be triggered ?

I know that I can set the button to .isEnabled = false but that is not a suitable solution since the grey background is created in a parent view controller from witch all my views are inheriting.

I set the grey view to .isUserInteractionEnabled = true (even if it's the default value) as stated here : In Stackoverflow

I thought using Apple Documentation but the problem is that there are a button and a gesture recognizer instead of multiple gesture recognizers.

Any idea on how to do this properly?

2 Answers

@Daljeet lead me to the solution. Using the Debug View Hierarchy I realised that my button was on top of the the view. My mistake was because I didn't realised the difference between UIView.layer.zPosition and the order of subviews in the hierarchy. A view can be drawn behind another view but nonetheless be in front in the View Hierarchy.

The solution was to use view.bringSubviewToFront(greyView)

Be aware that if you add the button after calling that last line of code, the button will be placed on top of the View Hierarchy

you can use this method to check if the touch was on the button and then trigger your action.

override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if let firstTouch = touches.first {
    let hitView = self.view.hitTest(firstTouch.location(in: yourBtn), with: event)

    if hitView === yourBtn {
        print("touch is inside")
    } else {
        print("touch is outside")
    }
 }
}
Related