首页 文章

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

提问于
浏览
28

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

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

    }
}

13 回答

  • 6

    喔好吧 . 所以我担心使用presentModalViewController:方法不太可能 . 一个"modal"视图/窗口/消息框/等的整个想法 . pp . is 用户 cannot 除了处理任何视图/窗口/消息框/等之外还做任何其他事情 . pp . 希望他/她做 .

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

  • 6

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

    这是你如何做到的:

    您需要向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];
                }
            }
        }
    }
    

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

  • 25

    我通过向手势识别器添加委托解决了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

  • 4

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

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

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

    这是我的版本适用于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];
            }
        }
    }
    
  • 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;
    }
    
  • 2

    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
    }
    

    }

  • -2

    基于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)
    
  • 69

    @yershuachu's answer,在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
        }
    
    }
    
  • 14

    斯威夫特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
    }
    

    }

  • 0

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

    我制作了一个自动关闭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
    
  • 4
    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
            }
        }
    }
    

相关问题