Dismiss modal view form sheet controller on outside tap
I know this is an old question but this IS possible, despite of what the "right" answer says. Since this was the first result when I was looking for this I decided to elaborate:
This is how you do it:
You need to add a property to the View Controller from where you want to present modally, in my case "tapBehindGesture".
then in viewDidAppear
if(!tapBehindGesture) { tapBehindGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapBehindDetected:)]; [tapBehindGesture setNumberOfTapsRequired:1]; [tapBehindGesture setCancelsTouchesInView:NO]; //So the user can still interact with controls in the modal view }[self.view.window addGestureRecognizer:tapBehindGesture];
And Here is the implementation for tapBehindDetected
- (void)tapBehindDetected:(UITapGestureRecognizer *)sender{ if (sender.state == UIGestureRecognizerStateEnded) { //(edited) not working for ios8 above //CGPoint location = [sender locationInView:nil]; //Passing nil gives us coordinates in the window CGPoint location = [sender locationInView: self.presentingViewController.view]; //Convert tap location into the local view's coordinate system. If outside, dismiss the view. if (![self.presentedViewController.view pointInside:[self.presentedViewController.view convertPoint:location fromView:self.view.window] withEvent:nil]) { if(self.presentedViewController) { [self dismissViewControllerAnimated:YES completion:nil]; } } }}
Just remember to remove tapBehindGesture
from view.window
on viewWillDisappear
to avoid triggering handleTapBehind in an unallocated object.
I solved iOS 8 issue by adding delegate to gesture recognizer
[taprecognizer setDelegate:self];
with these responses
#pragma mark - UIGestureRecognizer Delegate- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer { return YES;}- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer { return YES;}- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch { return YES;}
that works for me with iOS 8 GM
As far as I can tell none of the answer seem to be working right away in every condition.
My solution (either inherit from it or paste it in):
@interface MyViewController () <UIGestureRecognizerDelegate>@property (strong, nonatomic) UITapGestureRecognizer *tapOutsideRecognizer;@end-(void)viewDidAppear:(BOOL)animated{ [super viewDidAppear:animated]; if (!self.tapOutsideRecognizer) { self.tapOutsideRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapBehind:)]; self.tapOutsideRecognizer.numberOfTapsRequired = 1; self.tapOutsideRecognizer.cancelsTouchesInView = NO; self.tapOutsideRecognizer.delegate = self; [self.view.window addGestureRecognizer:self.tapOutsideRecognizer]; }}-(void)viewWillDisappear:(BOOL)animated{ [super viewWillDisappear:animated]; // to avoid nasty crashes if (self.tapOutsideRecognizer) { [self.view.window removeGestureRecognizer:self.tapOutsideRecognizer]; self.tapOutsideRecognizer = nil; }}#pragma mark - Actions - (IBAction)close:(id)sender{ [self dismissViewControllerAnimated:YES completion:nil];}- (void)handleTapBehind:(UITapGestureRecognizer *)sender{ if (sender.state == UIGestureRecognizerStateEnded) { CGPoint location = [sender locationInView:nil]; //Passing nil gives us coordinates in the window //Then we convert the tap's location into the local view's coordinate system, and test to see if it's in or outside. If outside, dismiss the view. if (![self.view pointInside:[self.view convertPoint:location fromView:self.view.window] withEvent:nil]) { // Remove the recognizer first so it's view.window is valid. [self.view.window removeGestureRecognizer:sender]; [self close:sender]; } }}#pragma mark - Gesture Recognizer// because of iOS8- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{ return YES;}