UITapGestureRecognizer
iOS development
gesture recognition
subviews
Swift programming

UITapGestureRecognizer tap on self.view but ignore subviews

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

When a UITapGestureRecognizer is attached to self.view, taps on subviews can still participate in that recognizer unless you filter them out. If you want the recognizer to fire only when the user taps the root view itself, the usual solution is to use the gesture recognizer delegate and reject touches whose touch.view is a subview.

Why the Default Behavior Includes Subviews

Gesture recognizers work with UIKit hit-testing. If a tap lands on a button, label, or custom child view inside self.view, that touch still belongs to the overall view hierarchy rooted at self.view.

So adding a recognizer to self.view does not automatically mean:

  • trigger only on the exact background view

It often means:

  • observe taps anywhere in the part of the hierarchy where the recognizer is attached

That is why delegate filtering is the standard fix.

Use UIGestureRecognizerDelegate

Implement gestureRecognizer(_:shouldReceive:) and accept only touches whose view is exactly self.view:

swift
1import UIKit
2
3final class ViewController: UIViewController, UIGestureRecognizerDelegate {
4    override func viewDidLoad() {
5        super.viewDidLoad()
6
7        let tap = UITapGestureRecognizer(target: self, action: #selector(handleBackgroundTap))
8        tap.delegate = self
9        tap.cancelsTouchesInView = false
10        view.addGestureRecognizer(tap)
11    }
12
13    @objc private func handleBackgroundTap() {
14        print("Tapped the root view itself")
15    }
16
17    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
18        return touch.view === self.view
19    }
20}

The identity check === matters. It verifies that the touched view is the same instance as self.view, not a descendant.

Why cancelsTouchesInView Often Helps

If subviews such as buttons or table cells should continue to receive their normal touches, set:

swift
tap.cancelsTouchesInView = false

Without that, the gesture recognizer may interfere with subview interaction depending on how recognition resolves.

Ignoring Only Certain Subviews

Sometimes you do not want a strict root-view-only rule. You may only want to ignore taps on interactive controls:

swift
1func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
2    if touch.view is UIControl {
3        return false
4    }
5    return true
6}

This is useful for background-tap gestures that dismiss the keyboard while still allowing buttons to work normally.

Ignoring a Specific Subtree

If you want to ignore taps inside one specific container, use isDescendant(of:):

swift
1func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
2    if let touchedView = touch.view, touchedView.isDescendant(of: formContainerView) {
3        return false
4    }
5    return true
6}

This gives you more control than a simple equality check.

Typical Use Cases

This pattern is common when:

  • dismissing the keyboard on background taps
  • closing an overlay when the user taps outside content
  • handling background-only taps without breaking subview controls

The key is to define clearly what should count as "background" in your hierarchy.

Common Pitfalls

The biggest mistake is assuming a recognizer attached to self.view automatically ignores subviews. It does not.

Another mistake is forgetting to set the recognizer's delegate. Without that, shouldReceive never runs.

People also compare against the wrong view. touch.view is the deepest hit-tested view, so equality with self.view is a strict filter.

Finally, if subviews stop responding after adding the tap recognizer, check cancelsTouchesInView and your gesture-recognition interactions.

If multiple recognizers are active on the screen, also review simultaneous-recognition rules so your background tap recognizer does not accidentally compete with higher-priority gestures.

Summary

  • A tap recognizer on self.view can still observe taps on subviews.
  • Use UIGestureRecognizerDelegate and gestureRecognizer(_:shouldReceive:) to filter touches.
  • Return touch.view === self.view when only direct taps on the root view should count.
  • Set cancelsTouchesInView = false when subviews should keep their normal touch behavior.
  • Use type checks or descendant checks when you need more selective filtering.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.