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.