代码之家  ›  专栏  ›  技术社区  ›  Daniel

iphone sdk通过点击ipad外部的模式viewcontrollers

  •  53
  • Daniel  · 技术社区  · 15 年前

    我想在用户点击模式视图外部时关闭一个FormSheetPresentation模式视图控制器…我已经看到一些应用程序这样做了(例如iPad上的eBay),但是我不知道为什么当模式视图这样显示时下面的视图被禁止触摸(如他们可能会把它当作一个教皇?……有人有什么建议吗?

    14 回复  |  直到 9 年前
        1
  •  127
  •   junglecat    12 年前

    我迟到了一年,但这很简单。

    让模态视图控制器将手势识别器连接到视图的窗口:

    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 release];
    

    处理代码:

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

    就这样。他妈的,这是一个有用的和经常直觉的行为。

        2
  •  11
  •   Jann    15 年前

    如果其他应用程序允许通过单击其外部来取消视图,则它们不使用模式视图。 UIModalPresentationFormSheets 不能这样被解雇。(事实上,sdk3.2中的任何uimodal都不能)。只有 UIPopoverController 可以通过单击区域外的来解除。应用程序开发人员很有可能(尽管与苹果的ipad hig相比)将背景屏幕遮住,然后显示 弹出控制器 所以看起来像 uimodalPresentationFormsheets表格 (或其他uimodal视图)。

    […]uimodalPresentationCurrentContext样式允许视图控制器采用其父级的表示样式。在每个模式视图中,暗显区域显示底层内容,但不允许在该内容中轻敲。因此,与popover不同,模态视图必须仍然具有允许用户关闭模态视图的控件。

    有关详细信息,请参阅开发人员网站上的ipadprogrammingguide(第46页-“为模式视图配置表示样式”)

        3
  •  11
  •   Erich    10 年前

    对于ios 8,必须同时实现 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;
    }
    
        4
  •  10
  •   Andro Selva Anand Wadhwani    12 年前

    上面的代码工作得很好,但是我会将if语句改为,

        if (!([self.view pointInside:[self.view convertPoint:location fromView:self.view.window] withEvent:nil] || [self.navigationController.view pointInside:[self.navigationController.view convertPoint:location fromView:self.navigationController.view.window] withEvent:nil]))
    
        {
            // Remove the recognizer first so it's view.window is valid.
            [self.view.window removeGestureRecognizer:sender];
            [self dismissModalViewControllerAnimated:YES];
        }
    

    这样可以确保您仍然可以与导航栏进行交互,否则在导航栏中单击将取消模式视图。

        5
  •  9
  •   Community CDub    8 年前

    针对iOS 8更新的答案

    显然,在iOS8中, UIDimmingView 有一个轻敲手势识别器,它会干扰初始实现,所以我们忽略它,不要求它失败。


    现在是速度的时代,所以大多数人可能只是在复制上面的代码。但是,不幸的是,在代码方面,我患有强迫症。

    Here is a modular solution that uses Danilo Campos's answer with categories . 它也 解决 一个重要的错误,可能会发生,如果你是通过其他方式解散你的模态, as mentioned .

    注: if语句出现是因为我对iphone和ipad都使用view控制器,而且只有ipad需要注册/注销。

    更新: 要点已经更新了,因为它不能正确地与可怕的 FCOverlay 代码,它不允许在显示的视图中识别手势。这些问题已经解决了。 使用类别非常简单:

    - (void)viewDidAppear:(BOOL)animated
    {
        [super viewDidAppear:animated];
    
        if (self.presentingViewController) {
            [self registerForDismissOnTapOutside];
        }
    }
    
    - (void)viewWillDisappear:(BOOL)animated
    {
        if (self.presentingViewController) {
            [self unregisterForDismissOnTapOutside];
        }
    
        [super viewWillDisappear:animated];
    }
    
        6
  •  8
  •   Samidjo    12 年前

    复制并将此代码粘贴到modalviewcontroller中:

    - (void) viewDidAppear:(BOOL)animated
    {
        [super viewDidAppear:animated];
        //Code for dissmissing this viewController by clicking outside it
        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];
    
    }
    
    - (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 dismissModalViewControllerAnimated:YES];
            }
        }
    }
    
        7
  •  3
  •   Digital_Reality    11 年前

    Very important: 如果你有其他方法关闭你的 模式弹出窗口 ,不要忘了移除轻敲手势识别器!

    我忘了这件事,后来发生了疯狂的车祸,因为窃听器仍在触发事件。

        8
  •  2
  •   Raymond Wang    13 年前

    根据苹果的iOS Hig,1.模态视图本身不具备在没有任何输入的情况下被忽略的能力;在需要用户输入的情况下使用模态视图。

        9
  •  1
  •   yhlin    10 年前

    请改用uiPresentationcontroller:

    - (void)presentationTransitionWillBegin
    {
        [super presentationTransitionWillBegin];
        UITapGestureRecognizer *dismissGesture=[[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(dismissGestureTapped:)];
        [self.containerView addGestureRecognizer:dismissGesture];
    
        [[[self presentedViewController] transitionCoordinator] animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) {
        } completion:nil];
    }
    - (void) dismissGestureTapped:(UITapGestureRecognizer *)sender{
        if (sender.state==UIGestureRecognizerStateEnded&&!CGRectContainsPoint([self frameOfPresentedViewInContainerView], [sender locationInView:sender.view])) {
            [self.presentingViewController dismissViewControllerAnimated:YES completion:nil];
        }
    }
    

    从lookinside示例修改

        10
  •  1
  •   Alexandre G    10 年前

    这对我来说很适合ios7和导航栏。

    如果不需要导航条,只需删除if语句中管道后面的location2和second条件即可。

    @假装这对你也有用

    - (void)handleTapBehind:(UITapGestureRecognizer *)sender
    {
        if (sender.state == UIGestureRecognizerStateEnded)
        {
            CGPoint location1 =  [sender locationInView:self.view];
            CGPoint location2 = [sender locationInView:self.navigationController.view];
    
            if (!([self.view pointInside:location1 withEvent:nil] || [self.navigationController.view pointInside:location2 withEvent:nil])) {
                [self.view.window removeGestureRecognizer:self.recognizer];
                [self dismissViewControllerAnimated:YES completion:nil];
            }
        }
    }
    

    编辑:您可能还需要是一个手势识别器代表,才能使用此解决方案和其他上述解决方案。这样做:

    @interface CommentTableViewController () <UIGestureRecognizerDelegate>
    

    将自己设置为识别器的代理:

    self.recognizer.delegate = self;
    

    并实现此委托方法:

    - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
        return YES;
    }
    
        11
  •  0
  •   Community CDub    8 年前

    这是相当可行的。

    看看这里

    https://stackoverflow.com/a/26016458/4074557

    它是一个导航控制器(模式),可以自动关闭ipad(当你点击外部时)

    在其中使用viewcontroller。

    希望有帮助。

        12
  •  0
  •   orafaelreis    10 年前

    我知道已经很晚了,但是可以考虑使用cleanmodel(使用ios 7和ios 8进行测试)。

    https://github.com/orafaelreis/CleanModal

        13
  •  0
  •   Maxim Pavlov    10 年前

    你可以使用 MZFormSheetController 这样地:

    MZFormSheetController *formSheet = [[MZFormSheetController alloc] initWithSize:customSize viewController:presentedViewController];
    formSheet.shouldDismissOnBackgroundViewTap = YES;
    [presentingViewController mz_presentFormSheetController:formSheet animated:YES completionHandler:nil];
    
        14
  •  0
  •   Roland Krinner    9 年前

    在swift 2/xcode版本7.2(7c68)中,以下代码对我有效。

    注意:此代码应该放在显示的表单或页面的viewcontroller.swift文件中,这里是:“pagesheetviewcontroller.swift”

    class PageSheetViewController: UIViewController, UIGestureRecognizerDelegate {
    
        override func viewDidAppear(animated: Bool) {
            let recognizer = UITapGestureRecognizer(target: self, action:Selector("handleTapBehind:"))
            recognizer.delegate = self
            recognizer.numberOfTapsRequired = 1
            recognizer.cancelsTouchesInView = false
            self.view.window?.addGestureRecognizer(recognizer)
        }
    
        func gestureRecognizer(sender: UIGestureRecognizer,
            shouldRecognizeSimultaneouslyWithGestureRecognizer:UIGestureRecognizer) -> Bool {
                return true
        }
    
        func handleTapBehind(sender:UIGestureRecognizer) {
            if(sender.state == UIGestureRecognizerState.Ended){
                var location:CGPoint = sender.locationInView(nil)
    
                // detect iOS Version 8.0 or greater
                let Device = UIDevice.currentDevice()
                let iosVersion = Double(Device.systemVersion) ?? 0
                let iOS8 = iosVersion >= 8
    
                if (iOS8) {
                    // in landscape view you will have to swap the location coordinates
                    if(UIInterfaceOrientationIsLandscape(UIApplication.sharedApplication().statusBarOrientation)){
                        location = CGPointMake(location.y, location.x);
                    }
                }
    
                if(!self.view.pointInside(self.view.convertPoint(location, fromView: self.view.window), withEvent: nil)){
                    self.view.window?.removeGestureRecognizer(sender)
                    self.dismissViewControllerAnimated(true, completion: nil)
                }
            }
        }
    }