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.

modal-example

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