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:21

Here is my version that works for iOS 7 and iOS 8 and does not require conditional swapping of coordinates:

- (void)handleTapBehind:(UITapGestureRecognizer *)sender
{
    if (sender.state == UIGestureRecognizerStateEnded)
    {
        CGPoint location = [sender locationInView:self.view];

        if (![self.view pointInside:location withEvent:nil]) {
            [self.view.window removeGestureRecognizer:self.recognizer];
            [self dismissViewControllerAnimated:YES completion:nil];
        }
    }
}
查看更多
戒情不戒烟
3楼-- · 2019-01-08 17:23

Swift 3

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: #selector(self.handleTapBehind))
        self.tapOutsideRecognizer.numberOfTapsRequired = 1
        self.tapOutsideRecognizer.cancelsTouchesInView = false
        self.tapOutsideRecognizer.delegate = self
        appDelegate.window?.addGestureRecognizer(self.tapOutsideRecognizer)
    }
}

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

      if(self.tapOutsideRecognizer != nil) {
        appDelegate.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: nil)

        if (!self.view.point(inside: self.view.convert(location, from: self.view.window), with: nil)) {
            self.view.window?.removeGestureRecognizer(sender)
            self.close(sender: sender)
        }
    }
}

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

}

查看更多
虎瘦雄心在
4楼-- · 2019-01-08 17:24

Based on Bart van Kuik's answer and NavAutoDismiss and other great snippets here.

class DismissableNavigationController: UINavigationController, UIGestureRecognizerDelegate {
    private var tapOutsideRecognizer: UITapGestureRecognizer!

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

        if tapOutsideRecognizer == nil {
            tapOutsideRecognizer = UITapGestureRecognizer(target: self, action: #selector(DismissableNavigationController.handleTapBehind))
            tapOutsideRecognizer.numberOfTapsRequired = 1
            tapOutsideRecognizer.cancelsTouchesInView = false
            tapOutsideRecognizer.delegate = self
            view.window?.addGestureRecognizer(tapOutsideRecognizer)
        }
    }

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

        if tapOutsideRecognizer != nil {
            view.window?.removeGestureRecognizer(tapOutsideRecognizer)
            tapOutsideRecognizer = nil
        }
    }

    func close(sender: AnyObject) {
        dismissViewControllerAnimated(true, completion: nil)
    }

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

            if UIInterfaceOrientationIsLandscape(UIApplication.sharedApplication().statusBarOrientation) {
                location = CGPoint(x: location.y, y: location.x)
            }

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

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

Usage:

let vc = MyViewController()
let nc = DismissableNavigationController(rootViewController: vc)
nc.modalPresentationStyle = UIModalPresentationStyle.FormSheet
presentViewController(nc, animated: true, completion: nil)
查看更多
该账号已被封号
5楼-- · 2019-01-08 17:26

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

查看更多
我想做一个坏孩纸
6楼-- · 2019-01-08 17:29

For iOS 8, you must both implement the UIGestureRecognizer per Martino's answer, and swap the (x,y) coordinates of the tapped location when in landscape orientation. Not sure if this is due to an iOS 8 bug.

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

    // add gesture recognizer to window

    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.window addGestureRecognizer:recognizer];
    recognizer.delegate = self;
}

- (void)handleTapBehind:(UITapGestureRecognizer *)sender
{
    if (sender.state == UIGestureRecognizerStateEnded) {

        // passing nil gives us coordinates in the window
        CGPoint location = [sender locationInView:nil];

        // swap (x,y) on iOS 8 in landscape
        if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"8.0")) {
            if (UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation)) {
                location = CGPointMake(location.y, location.x);
            }
        }

        // 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 dismissViewControllerAnimated:YES completion:nil];
        }
    }
}


#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;
}
查看更多
SAY GOODBYE
7楼-- · 2019-01-08 17:29
extension CGPoint {
    mutating func correctOrientation() {
        let screenSize = UIScreen.mainScreen().bounds
        switch(UIDevice.currentDevice().orientation) {
        case .Portrait:
            break
        case .PortraitUpsideDown:
            y = screenSize.height - y
            break
        case .LandscapeLeft:
            swap(&y, &x)
            y = screenSize.height - y
            break
        case .LandscapeRight:
            swap(&y, &x)
            x = screenSize.width - x
            break
        default:
            break
        }
    }
}
查看更多
登录 后发表回答