The default behavior is for your tap or other gestures to bubble up to their child controls. This avoids the need to add a recognizer on all of your child controls. But this isn’t always the behavior you are looking for. For example imagine you create the below modal view. You want to add a tap gesture recognizer to the view itself so when your user taps the grey area it closes the modal. But you don’t want the gesture to be triggered when a tap is made on the content UIView of the modal.

First adding the UITapGestureRecognizer
For our use case the first thing we need to do is add a UITapGestureRecognizer to the root UIView of the UIViewController. This will close our UIViewController when the grey area is tapped.
| internal class ConfirmationViewController: UIViewController { | |
| override func viewDidLoad() { | |
| super.viewDidLoad() | |
| let tap = UITapGestureRecognizer(target: self, action: #selector(self.handleTapOffModal(_:))) | |
| tap.delegate = self | |
| view.addGestureRecognizer(tap) | |
| view.isUserInteractionEnabled = true | |
| } | |
| } |
Limiting the scope of the Gesture
So that the UITapGestureRecognizer isn’t triggered when a tap is made to the content UIView we simply need to add protocol method to restrict the tap to the view it is associated with.
| extension ConfirmationViewController: UIGestureRecognizerDelegate { | |
| func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool { | |
| return touch.view == gestureRecognizer.view | |
| } | |
| func handleTapOffModal(_ sender: UITapGestureRecognizer) { | |
| dismiss(animated: true, completion: nil) | |
| } | |
| } |
Putting it all together
Below is the full code associated with the modal UIViewController. If you are not familiar with how to create a modal UIViewController I would recommend checking out Tim Sanders tutorial here.
| import UIKit | |
| internal class ConfirmationViewController: UIViewController { | |
| override func viewDidLoad() { | |
| super.viewDidLoad() | |
| let tap = UITapGestureRecognizer(target: self, action: #selector(self.handleTapOffModal(_:))) | |
| tap.delegate = self | |
| view.addGestureRecognizer(tap) | |
| view.isUserInteractionEnabled = true | |
| } | |
| } | |
| extension ConfirmationViewController { | |
| @IBAction func cancel_click(_ sender: Any) { | |
| dismiss(animated: true, completion: nil) | |
| } | |
| @IBAction func confirm_click(_ sender: Any) { | |
| print("Perform Confirmation action") | |
| dismiss(animated: true, completion: nil) | |
| } | |
| } | |
| extension ConfirmationViewController: UIGestureRecognizerDelegate { | |
| func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool { | |
| return touch.view == gestureRecognizer.view | |
| } | |
| func handleTapOffModal(_ sender: UITapGestureRecognizer) { | |
| dismiss(animated: true, completion: nil) | |
| } | |
| } |