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

jes*_*ssy 32 xcode objective-c 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];
        }

    }
}
Run Code Online (Sandbox Code Playgroud)

Are*_*res 72

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

这是你如何做到的:

您需要向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];
Run Code Online (Sandbox Code Playgroud)

这是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];
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

只记得删除tapBehindGestureview.windowviewWillDisappear避免未分配的对象触发handleTapBehind.

  • 有谁知道如何解决iOS8的问题?或者是否有更好的解决方案? (3认同)
  • 效果很好(iOS7).其他使用此代码的人应该注意`self.presentedViewController.view`.如果在模态视图控制器中实现手势,您应该只使用`self.view`. (2认同)
  • @Ares我也有问题iOS8作为凯文.我知道我们不应该在这里讨论beta版本,请点击此链接:https://devforums.apple.com/thread/243768 (2认同)
  • 我通过以下代码更改了位置:CGPoint location = [tapSender locationInView:self.presentingViewController.view]; (2认同)

Mar*_*oli 26

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

[taprecognizer setDelegate:self];
Run Code Online (Sandbox Code Playgroud)

有这些回应

#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;
}
Run Code Online (Sandbox Code Playgroud)

这适用于iOS 8 GM


yer*_*chu 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;
}
Run Code Online (Sandbox Code Playgroud)


jix*_*xam 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];
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 奇迹般有效! (2认同)

小智 12

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
}
Run Code Online (Sandbox Code Playgroud)

}


Eri*_*ich 10

对于iOS 8,您必须同时实现UIGestureRecognizer每个Martino的答案,并在横向方向上交换轻敲位置的(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;
}
Run Code Online (Sandbox Code Playgroud)


Bar*_*uik 6

@ yershuachu的回答,在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
    }

}
Run Code Online (Sandbox Code Playgroud)


neo*_*eye 6

基于Bart van Kuik的回答NavAutoDismiss以及其他伟大的片段.

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
    }
}
Run Code Online (Sandbox Code Playgroud)

用法:

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


cli*_*hlt 2

喔好吧。所以恐怕使用 presentModalViewController: 方法不太可能。“模态”视图/窗口/消息框/等的整个想法。pp.用户除了处理视图/窗口/消息框/等之外不能做任何其他事情。pp. 希望他/她做。

相反,您想要做的不是呈现模式视图控制器,而是以常规方式加载并显示表单视图控制器。请注意,在主控制器中,表单仅显示 BOOL 变量,然后处理可能发生的任何点击。如果您的表单正在显示,请将其关闭。