I\'ve been trying to dismiss the modal form sheet view on outside tap on iOS 8 with no luck, I\'ve tried this code
UITapGestureRecognizer *recognizer = [[U
There are actually two problems in iOS 8. First, the gesture recognition does not begin.
I solved this by adding the UIGestureRecognizerDelegate protocol and implementing
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer*)otherGestureRecognizer
{
return YES;
}
Also, don't forget to register the delegate with
recognizer.delegate = self;
Now the gesture recognizer should recognize gestures and the target method (handleTapBehind:) will be called.
Here comes the second problem in iOS 8: locationInView: doesn't seem to take the device orientation into account if nil is passed as a view. Instead, passing the root view works.
Here's my target code that seems to work for iOS 7.1 and 8.0:
if (sender.state == UIGestureRecognizerStateEnded) {
UIView *rootView = self.view.window.rootViewController.view;
CGPoint location = [sender locationInView:rootView];
if (![self.view pointInside:[self.view convertPoint:location fromView:rootView] withEvent:nil]) {
[self dismissViewControllerAnimated:YES completion:^{
[self.view.window removeGestureRecognizer:sender];
}];
}
}
Swift 3.1 solution that works in both portrait and landscape.
class TapBehindModalViewController: UIViewController, UIGestureRecognizerDelegate {
private var tapOutsideRecognizer: UITapGestureRecognizer!
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if(self.tapOutsideRecognizer == nil) {
self.tapOutsideRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.handleTapBehind))
self.tapOutsideRecognizer.numberOfTapsRequired = 1
self.tapOutsideRecognizer.cancelsTouchesInView = false
self.tapOutsideRecognizer.delegate = self
self.view.window?.addGestureRecognizer(self.tapOutsideRecognizer)
}
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if(self.tapOutsideRecognizer != nil) {
self.view.window?.removeGestureRecognizer(self.tapOutsideRecognizer)
self.tapOutsideRecognizer = nil
}
}
func close(sender: AnyObject) {
self.dismiss(animated: true, completion: nil)
}
// MARK: - Gesture methods to dismiss this with tap outside
func handleTapBehind(sender: UITapGestureRecognizer) {
if (sender.state == UIGestureRecognizerState.ended) {
let location: CGPoint = sender.location(in: self.view)
if (!self.view.point(inside: location, with: nil)) {
self.view.window?.removeGestureRecognizer(sender)
self.close(sender: sender)
}
}
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
}
In iOS 8, You can look at using the new UIPresentationController class. It gives you better control over the container around your custom view controller presentation (allowing you to correctly add a gesture recogniser of your own).
Here is a link to quite a simple tutorial as well: http://dativestudios.com/blog/2014/06/29/presentation-controllers/
Then add the dimming view tap-to-dismiss:
UITapGestureRecognizer *singleFingerTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleSingleTap:)];
[self.dimmingView addGestureRecognizer:singleFingerTap];
- (void)handleSingleTap:(UITapGestureRecognizer *)recognizer {
[self.presentingViewController dismissViewControllerAnimated:YES completion:nil];
}