Dismiss modal view form sheet controller on outsid

2019-01-08 16:32发布

I am presenting a modal view controller as a form sheet and dismissing it when the cancel button, which is a bar button item, is clicked. I need to dismiss it when I tap on outside of that view. Please help me with a reference. Note: my modal view controller is presented with a navigation controller.

@cli_hlt, @Bill Brasky thanks for your answer. I need to dismiss it when tap occurs outside of the modal view which is a form sheet. I am pasting my code below.

-(void)gridView:(AQGridView *)gridView didSelectItemAtIndex:(NSUInteger)index  
{        
    if(adminMode) 
    {
        CHEditEmployeeViewController *editVC = [[CHEditEmployeeViewController alloc] initWithNibName:@"CHEditEmployeeViewController" bundle:nil];
        editVC.delegate = self;
        editVC.pickedEmployee = employee;
        editVC.edit = TRUE;
        editVC.delegate = self;
        UINavigationController *navigationController = [[UINavigationController alloc]initWithRootViewController:editVC];
        navigationController.modalPresentationStyle = UIModalPresentationFormSheet;
        [self presentModalViewController:navigationController animated:YES];

        return;
    }   //the above code is from the view controller which presents the modal     view. Please look at the below code too which is from my modal view controller. Please guide me in a proper way.   -(void)tapGestureRecognizer {

    UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapBehind:)];
    [recognizer setNumberOfTapsRequired:1];
    recognizer.cancelsTouchesInView = NO; //So the user can still interact with controls in the modal view
    [self.view addGestureRecognizer:recognizer];

}

- (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]) 
        {
            [self dismissModalViewControllerAnimated:YES];
            [self.view.window removeGestureRecognizer:sender];
        }

    }
}

13条回答
一夜七次
2楼-- · 2019-01-08 17:08

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.

查看更多
成全新的幸福
3楼-- · 2019-01-08 17:14

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;
}
查看更多
女痞
4楼-- · 2019-01-08 17:16

Ah ok. So I'm afraid thats not quite possible using the presentModalViewController: method. The whole idea of a "modal" view/window/message box/etc. pp. is that the user cannot do anything else than processing whatever the view/window/message box/etc. pp. wants him/her to do.

What you want to do instead is not present a modal view controller, but rather load and show your form view controller the regular way. Note in your master controller that the form is just showing e.g. with a BOOL variable and then handle there any taps that might occur. If your form is showing, dismiss it.

查看更多
Fickle 薄情
5楼-- · 2019-01-08 17:17

@yershuachu's answer, in Swift 2:

class ModalParentViewController: UIViewController, UIGestureRecognizerDelegate {

    private var tapOutsideRecognizer: UITapGestureRecognizer!

    override func viewDidAppear(animated: Bool) {
        super.viewDidAppear(animated)

        if(self.tapOutsideRecognizer == nil) {
            self.tapOutsideRecognizer = UITapGestureRecognizer(target: self, action: "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.dismissViewControllerAnimated(true, completion: nil)
    }

    func handleTapBehind(sender: UITapGestureRecognizer) {
        if (sender.state == UIGestureRecognizerState.Ended) {
            let location: CGPoint = sender.locationInView(nil)

            if (!self.view.pointInside(self.view.convertPoint(location, fromView: self.view.window), withEvent: nil)) {
                self.view.window?.removeGestureRecognizer(sender)
                self.close(sender)
            }
        }
    }

    func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        return true
    }

}
查看更多
叛逆
6楼-- · 2019-01-08 17:17

I made a navigationController that auto dismiss for iPad

https://github.com/curciosobrinho/NavAutoDismiss

It is just the same code as above, working on iOS 8.

So, all the credits go to the people above.

I just did it to be more generic and easier to use.

You just need to copy BOTH files to your project

Import the header file (.h)

And use your viewcontroller (that you want to show) as the rootViewController.

How to use it:

//Import the .h file
#import "NavDismissViewController.h"

//Instanciate your view controller (the view you want to present)

YourViewController * yourVC = [YourViewController new];

//Instanciate the NavDismissViewController with your view controller as the rootViewController

NavDismissViewController *nav = [[NavDismissViewController alloc] initWithRootViewController:yourVC];

//if you want to change the navigationBar translucent behaviour

[nav.navigationBar setTranslucent:NO];

//Choose the Modal style

nav.modalPresentationStyle=UIModalPresentationFormSheet;

//present your controller

[self presentViewController:nav animated:YES completion:nil];

//Done
查看更多
你好瞎i
7楼-- · 2019-01-08 17:20

I use it in this form without any problems neither on iOS 7.1 nor iOS 8.3.

- (void)viewDidAppear:(BOOL)animated
{
     [super viewDidAppear:animated];

     [self.view.window addGestureRecognizer:self.tapBehindGesture];
}

- (void)viewWillDisappear:(BOOL)animated
{
     [super viewWillDisappear:animated];

     [self.view.window removeGestureRecognizer:self.tapBehindGesture];
}

- (UITapGestureRecognizer*)tapBehindGesture
{    
    if (_tapBehindGesture == nil)
    {
        _tapBehindGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapBehindRecognized:)];
        _tapBehindGesture.numberOfTapsRequired = 1;
        _tapBehindGesture.cancelsTouchesInView = NO;
        _tapBehindGesture.delegate = self;
    }

    return _tapBehindGesture;
}

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
    return YES;
}
查看更多
登录 后发表回答