Dismiss modal view form sheet controller on outside tap

前端 未结 13 1010
猫巷女王i
猫巷女王i 2020-12-01 06:16

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

相关标签:
13条回答
  • 2020-12-01 06:45

    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
    }
    

    }

    0 讨论(0)
  • 2020-12-01 06:46

    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

    0 讨论(0)
  • 2020-12-01 06:48

    @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
        }
    
    }
    
    0 讨论(0)
  • 2020-12-01 06:52

    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.

    0 讨论(0)
  • 2020-12-01 06:54

    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;
    }
    
    0 讨论(0)
  • 2020-12-01 06:54

    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)
    
    0 讨论(0)
提交回复
热议问题