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