在外部水龙头上关闭模态视图表单控制器

时间:2012-02-01 20:07:13

标签: objective-c xcode uinavigationcontroller modalviewcontroller

我将模态视图控制器呈现为表单,并在单击取消按钮(即按钮项目)时将其解除。当我在该视图之外点击时,我需要忽略它。请帮我一个参考。注意:我的模态视图控制器带有导航控制器。

@cli_hlt,@ Bill Brasky感谢您的回答。当在作为表单的模态视图之外发生敲击时,我需要忽略它。我在下面粘贴我的代码。

-(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];
        }

    }
}

14 个答案:

答案 0 :(得分:70)

我知道这是一个古老的问题,但这是可能的,尽管“正确”的答案说的是。由于这是我在寻找这个时的第一个结果,所以我决定详细说明:

您就是这样做的:

您需要向View Controller添加一个属性,您希望以模态方式呈现该属性,在我的情况下为“tapBehindGesture”。

然后在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];

以下是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];
            }
        }
    }
}

请记住在tapBehindGesture上从view.window删除viewWillDisappear,以避免在未分配的对象中触发handleTapBehind。

答案 1 :(得分:26)

我通过向手势识别器添加委托来解决iOS 8问题

[taprecognizer setDelegate:self];

有了这些回复

#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;
}

适用于iOS 8 GM

答案 2 :(得分:16)

据我所知,答案似乎没有在任何情况下立即起作用。

我的解决方案(从中继承或粘贴):

@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;
}

答案 3 :(得分:14)

这是我的版本适用于iOS 7和iOS 8,不需要条件交换坐标:

- (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];
        }
    }
}

答案 4 :(得分:10)

对于iOS 8,您必须同时实施每个Martino的答案UIGestureRecognizer,并在横向时交换点击位置的(x,y)坐标。不确定这是否是由于iOS 8错误造成的。

- (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;
}

答案 5 :(得分:10)

Swift 4版本在纵向和横向都有效 - 不需要交换x,y。

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
@objc 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
}

}

答案 6 :(得分:6)

基于Bart van Kuik's answerNavAutoDismiss以及此处的其他精彩摘要。

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
    }
}

用法:

let vc = MyViewController()
let nc = DismissableNavigationController(rootViewController: vc)
nc.modalPresentationStyle = UIModalPresentationStyle.FormSheet
presentViewController(nc, animated: true, completion: nil)

答案 7 :(得分:4)

好的,好的。所以我担心使用presentModalViewController:方法不太可能。 “模态”视图/窗口/消息框/等的整个想法。 pp。,用户不能做任何事情而不是处理任何视图/窗口/消息框/等。 pp。希望他/她做。

您想要做的事情不是模态视图控制器,而是以常规方式加载并显示表单视图控制器。请注意主控制器中的表单只显示例如使用BOOL变量,然后处理可能发生的任何点击。如果您的表单正在显示,请将其解雇。

答案 8 :(得分:4)

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
}

}

答案 9 :(得分:2)

我在这种形式下使用它,在iOS 7.1和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;
}

答案 10 :(得分:0)

我制作了一个自动关闭iPad的navigationController

https://github.com/curciosobrinho/NavAutoDismiss

它与上面的代码相同,适用于iOS 8。

所以,所有的学分都交给了上面的人。

我只是做得更通用,更容易使用。

您只需要将BOTH文件复制到项目中

导入头文件(.h)

并使用您的viewcontroller(您想要显示)作为rootViewController。

如何使用它:

//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

答案 11 :(得分:0)

FYI,现在在iOS 13表单上具有标准的关闭手势-当您触摸模式框内的任意位置并将其向下滑动时,手势就会消失(如果需要,将新的isModalInPresentation属性设置为false以防止这种情况。)

答案 12 :(得分:0)

基于一些问题并进行了一些改进和更新:

- (void)tapOutsideDetected:(UITapGestureRecognizer *)sender {
    if (@available(iOS 13, *)) {} else {
        UIView *aView = self.navigationController ? self.navigationController.view : self.view;
        CGPoint location = [sender locationInView: aView];
        
        if (![aView pointInside:location withEvent:nil]) {
            [self removeGestureRecognizer:sender];
            [self dismissViewControllerAnimated:YES completion:nil];
        }
    }
}

地点:

- (void) removeGestureRecognizer:(UIGestureRecognizer *) gesture {
    if (self.recognizer) {
        UIView *aView = self.recognizer.view;
        if (aView) {
            [aView removeGestureRecognizer:self.recognizer];
        }
    }
}

在本例中,UITapGestureRecognizer 已应用于 UIViewController

答案 13 :(得分:-2)

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
        }
    }
}
相关问题