Detect Touch outside a view

There are tasks that are not so simple (for me) to do on iOS. One of them is to detect a tap outside a view, for example to dismiss a modal dialog.

So, for reference, here’s how I implemented it.

First: you present a modal sheet using:

- (void)presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion

In your interface, declare an UITapGestureRecognizer

@property (nonatomic, strong) UITapGestureRecognizer *gesture;

Then, in your implementation:

//==========================================================
- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    self.gesture = [[UITapGestureRecognizer alloc] 
      initWithTarget:self action:@selector(handleTap:)];
    self.gesture.numberOfTapsRequired = 1;
    self.gesture.numberOfTouchesRequired = 1;
    self.gesture.delegate = self;
    [self.gesture setCancelsTouchesInView:NO];
    [self.view.window addGestureRecognizer:self.gesture];
}

//==========================================================
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
      shouldReceiveTouch:(UITouch *)touch
{
    CGPoint point = [touch locationInView:nil];

    UIWindow *mainWindow = [[[UIApplication sharedApplication]
        delegate] window];
    CGPoint pointInSubview = [self.view convertPoint:point 
       fromView:mainWindow];
    if (!CGRectContainsPoint(self.view.frame, pointInSubview)) {
        [self close:self];
        return NO;
    }
    return YES; // handle the touch
}

//==========================================================
- (void)handleTap:(UITapGestureRecognizer *)gestureRecognizer
{
    if (gestureRecognizer.state != UIGestureRecognizerStateEnded) {
        return;
    }
    // Handle the tap if you want to
}

//==========================================================
#pragma mark - Close modal
- (IBAction)close:(id)sender
{
    self.gesture.delegate = nil;
    [self.view.window removeGestureRecognizer:self.gesture];
    [self dismissViewControllerAnimated:YES completion:^{
    }];
}

That’s it. It could be easier.

Here’s a link to a Gist on GitHub, easier to read.

Leave a Reply

Your email address will not be published. Required fields are marked *

*