【问题标题】:Is it possible to determine whether ViewController is presented as Modal?是否可以确定 ViewController 是否显示为 Modal?
【发布时间】:2011-02-17 10:12:18
【问题描述】:

是否可以在 ViewController 类内部检查它是否显示为模态视图控制器?

【问题讨论】:

    标签: iphone ios view controller modal-dialog


    【解决方案1】:

    由于 modalViewController 在 iOS 6 中已被弃用,这里有一个适用于 iOS 5+ 的版本,并且编译时不会出现警告。

    目标-C:

    - (BOOL)isModal {
        return self.presentingViewController.presentedViewController == self
          || (self.navigationController != nil && self.navigationController.presentingViewController.presentedViewController == self.navigationController)
          || [self.tabBarController.presentingViewController isKindOfClass:[UITabBarController class]];
    }
    

    斯威夫特:

    var isModal: Bool {
        return self.presentingViewController?.presentedViewController == self
            || (self.navigationController != nil && self.navigationController?.presentingViewController?.presentedViewController == self.navigationController)
            || self.tabBarController?.presentingViewController is UITabBarController
    }
    

    Felipe 回答的提示。

    【讨论】:

    • 很好,很长一段时间后我不得不再次使用它,并注意到发生了弃用......我编辑了我的答案,以便人们在使用 iOS 6+ 时开始在这里寻找正确的代码,谢谢
    • 如果父视图控制器是我们的视图控制器被推送的模式,则不起作用。
    • 有一个bug,我们应该检查双方是否为nil,因为nil == nil返回YES,这不是我们想要的结果。
    • @GabrielePetronella 你介意我更新答案以包括该方法的 Swift 实现吗?
    • @MichaelWaterfall 将不胜感激,谢谢
    【解决方案2】:

    如果您正在寻找 iOS 6+,此答案已弃用,您应该查看Gabriele Petronella's answer


    没有一种简洁的方法可以做到这一点,作为 UIKit 的原生属性或方法。您可以做的是检查控制器的多个方面,以确保它显示为模态。

    所以,要检查 current(在下面的代码中表示为 self)控制器是否以模态方式呈现,我在 UIViewController 类别中具有下面的函数,或者(如果您的项目不需要使用其他 UIKit 控制器,例如 UITableViewController)在我的其他控制器继承的基本控制器中

    -(BOOL)isModal {
    
         BOOL isModal = ((self.parentViewController && self.parentViewController.modalViewController == self) || 
                //or if I have a navigation controller, check if its parent modal view controller is self navigation controller
                ( self.navigationController && self.navigationController.parentViewController && self.navigationController.parentViewController.modalViewController == self.navigationController) || 
                //or if the parent of my UITabBarController is also a UITabBarController class, then there is no way to do that, except by using a modal presentation
                [[[self tabBarController] parentViewController] isKindOfClass:[UITabBarController class]]);
    
        //iOS 5+
        if (!isModal && [self respondsToSelector:@selector(presentingViewController)]) {
    
            isModal = ((self.presentingViewController && self.presentingViewController.modalViewController == self) || 
                 //or if I have a navigation controller, check if its parent modal view controller is self navigation controller
                 (self.navigationController && self.navigationController.presentingViewController && self.navigationController.presentingViewController.modalViewController == self.navigationController) || 
                 //or if the parent of my UITabBarController is also a UITabBarController class, then there is no way to do that, except by using a modal presentation
                 [[[self tabBarController] presentingViewController] isKindOfClass:[UITabBarController class]]);
    
        }
    
        return isModal;        
    
    }
    

    编辑: 我添加了最后一个检查以查看是否正在使用 UITabBarController,并且您将另一个 UITabBarController 呈现为模态。

    编辑 2:添加了 iOS 5+ 检查,其中 UIViewController 不再回答 parentViewController,而是回答 presentingViewController

    编辑 3:我为它创建了一个要点,以防万一https://gist.github.com/3174081

    【讨论】:

    • 请记住,自 iOS 6 起,modalViewController 属性已被弃用。文档建议改用 presentedViewController
    • @BartJacobs 好点! iOS6发布后我还没有看过这个答案,所以它可能不是最新的。我将在本周晚些时候尝试进行一些测试以更新它,tks!
    • NSLog(@"%@", self.navigationController.parentViewController) 打印 (null) - 你能解释一下原因吗?我的 ViewController 通过 storyboard 中的 navController 与模态视图控制器连接。
    • @oyatek 你可以使用 pastebin 或类似的东西并显示一些代码吗?
    • @Feilpe 我发现了问题 - .parentViewController 已弃用,必须改用 .presentingViewController
    【解决方案3】:

    在 iOS5+ 中,正如您在 UIViewController Class Reference 中看到的,您可以从属性“presentingViewController”中获取它。

    呈现视图控制器 呈现此视图控制器的视图控制器。 (只读)

    @property(nonatomic, readonly) UIViewController *presentingViewController
    讨论

    如果收到此消息的视图控制器由另一个视图控制器呈现,则此属性包含正在呈现它的视图控制器。如果未显示视图控制器,但正在显示其祖先之一,则此属性保存显示最近祖先的视图控制器。如果既没有显示视图控制器也没有显示它的任何祖先,则此属性为 nil。

    可用性
    适用于 iOS 5.0 及更高版本。
    声明于
    UIViewController.h

    【讨论】:

    • 完美运行,使用 if (self.presentingViewController) {//This is a modal viewContoller} else {//This is a normal ViewController}
    • 恕我直言,这是这里唯一的正确答案。只需检查是否存在presentingViewController。它也可以在容器视图控制器中工作,因为它会自动遍历祖先。
    【解决方案4】:

    如果没有,您可以在 UIViewController 子类中为此 (presentedAsModal) 定义一个属性,并将其设置为 YES,然后再将 ViewController 呈现为模态视图。

    childVC.presentedAsModal = YES;
    [parentVC presentModalViewController:childVC animated:YES];
    

    您可以在 viewWillAppear 覆盖中检查此值。

    我相信没有说明视图如何呈现的官方属性,但没有什么能阻止您创建自己的视图。

    【讨论】:

    • 对,这就是我所做的,但我正在寻找其他一些巧妙的解决方案。谢谢。
    • 如果您将UINavigationController 呈现为模态,则此解决方案不起作用...除非您创建自定义导航控制器只是为了添加此属性。之后,在控制器内部,每次您需要检查控制器是否显示为模态时,您都必须不断将 self.navigationController 转换为这个自定义类
    【解决方案5】:

    Petronella's answer 如果 self.navigationController 以模态方式呈现但 self 不等于 self.navigationController.viewControllers[0] 则不起作用,在这种情况下 self 被推送。

    这是解决问题的方法。

    return self.presentingViewController.presentedViewController == self
                || (self.navigationController != nil && self.navigationController.presentingViewController.presentedViewController == self.navigationController && self == self.navigationController.viewControllers[0])
                || [self.tabBarController.presentingViewController isKindOfClass:[UITabBarController class]];
    

    在 Swift 中:

    return self.presentingViewController?.presentedViewController == self
            || (self.navigationController != nil && self.navigationController?.presentingViewController?.presentedViewController == self.navigationController && self.navigationController?.viewControllers[0] == self)
            || self.tabBarController?.presentingViewController is UITabBarController
    

    【讨论】:

      【解决方案6】:

      这应该可行。

      if(self.parentViewController.modalViewController == self)…
      

      【讨论】:

      • 不幸的是,这不起作用。这是我的第一次尝试。但返回 modalViewController ins nil :(.
      • 如果你只是得到'self.parentViewController',它会返回正确的父对象吗?
      • 问题可能是您的 UIViewController 子类位于 UINavigationController 或 UITabBarController(或两者兼有)中,在这种情况下,您可能需要在视图层次结构中进一步挖掘以找出父级呈现为模态视图控制器。
      • @hgpc 我在我的项目中需要这个 chck,所以我只是添加了一个答案来检查 UINavigationControllerUITabBarController 情况。到目前为止,它工作得很好
      【解决方案7】:

      最好的检查方法

       if (self.navigationController.presentingViewController) {
               NSLog(@"Model Present");
          }
      

      【讨论】:

        【解决方案8】:

        如果您不需要区分全屏模态视图和非模态视图,我的项目就是这种情况(我正在处理一个仅在表单和页面表中出现的问题),您可以使用 UIViewController 的 modalPresentationStyle 属性:

        switch (self.modalPresentationStyle) {
            case 0: NSLog(@"full screen, or not modal"); break;
            case 1: NSLog(@"page sheet"); break;
            case 2: NSLog(@"form sheet"); break;
        }
        

        【讨论】:

          【解决方案9】:

          Swift 中:

          func isUIViewControllerPresentedAsModal() -> Bool {
              if((self.presentingViewController) != nil) {
                  return true
              }
          
              if(self.presentingViewController?.presentedViewController == self) {
                  return true
              }
          
              if(self.navigationController?.presentingViewController?.presentedViewController == self.navigationController) {
                  return true
              }
          
              if((self.tabBarController?.presentingViewController?.isKindOfClass(UITabBarController)) != nil) {
                  return true
              }
          
              return false
          }
          

          【讨论】:

          • 此用例存在问题。如果我在 UINavigationController 的根视图控制器中,它仍然返回 true 而没有任何模式呈现。
          • 第一个 if 语句涵盖了第二个 if 语句中的所有内容,从而使第二个语句变得多余。我不确定这里的意图是什么。
          【解决方案10】:

          在我的项目中,我有一个视图控制器(详细信息),可以由主视图控制器以模态方式(添加新项目时)或推送(编辑现有项目时)呈现。当用户点击 [完成] 时,详细视图控制器调用主视图控制器的方法来通知它已准备好关闭。 Master 必须确定 Detail 如何呈现才能知道如何关闭它。我就是这样做的:

          UIViewController *vc = self.navigationController.viewControllers.lastObject;
          if (vc == self) {
              [self dismissViewControllerAnimated:YES completion:NULL];
          } else {
              [self.navigationController popViewControllerAnimated:YES];
          }
          

          【讨论】:

            【解决方案11】:

            这样的 hack 可能会奏效。

            UIViewController* child = self;
            UIViewController* parent = child.parentViewController;
            while (parent && parent.modalViewController != child) {
                child = parent;
                parent = child.parentViewController;
            }
            if (parent) {
                // A view controller in the hierarchy was presented as a modal view controller
            }
            

            但是,我认为我之前的答案是一个更清洁的解决方案。

            【讨论】:

              【解决方案12】:

              对我有用的是:

              // this is the trick: set parent view controller as application's window root view controller
              UIApplication.sharedApplication.delegate.window.rootViewController = viewController;
              
              // assert no modal view is presented
              XCTAssertNil(viewController.presentedViewController);
              
              // simulate button tap which shows modal view controller
              [viewController.deleteButton sendActionsForControlEvents:UIControlEventTouchUpInside];
              
              // assert that modal view controller is presented
              XCTAssertEqualObjects(viewController.presentedViewController.class, MyModalViewController.class);
              

              据我测试,这适用于 iOS7 和 iOS8。但是没有在 iOS6 上尝试。

              【讨论】:

                【解决方案13】:

                我已经四处寻找这个问题的正确答案,但我找不到任何涵盖所有可能情况的答案。我写了这几行似乎可以完成这项工作的代码。您可以找到一些内联 cmets 来确定检查的内容。

                - (BOOL)isModal {
                    BOOL modal = NO;
                    if ([self presentingViewController]) { //Some view Controller is presenting the current stack
                        UIViewController *presented = [[self presentingViewController] presentedViewController]; // What's been presented
                        if ([presented respondsToSelector:@selector(viewControllers)]) { // There's a stack
                            NSArray *viewControllers = [presented performSelector:@selector(viewControllers)];
                            modal = [viewControllers firstObject] == self; // Current VC is presented modally if it's the first in the stack
                        }
                        else {
                            modal = presented == self; // Don't think this is actually needed. set modal = YES should do the job tho.
                        }
                    }
                    return modal;
                }
                

                希望对您有所帮助。

                【讨论】:

                  【解决方案14】:

                  这是我对@GabrielePetronella 的isModal 的修改版本,它与包含的视图控制器一起使用,因为它首先向上遍历 parentViewController 层次结构。还将代码拉到多行中,以便清楚它在做什么。

                  var isModal: Bool {
                      // If we are a child view controller, we need to check our parent's presentation
                      // rather than our own.  So walk up the chain until we don't see any parentViewControllers
                      var potentiallyPresentedViewController : UIViewController = self
                      while (potentiallyPresentedViewController.parentViewController != nil) {
                          potentiallyPresentedViewController = potentiallyPresentedViewController.parentViewController!
                      }
                  
                      if self.presentingViewController?.presentedViewController == potentiallyPresentedViewController {
                          return true
                      }
                  
                      if let navigationController = potentiallyPresentedViewController.navigationController {
                          if navigationController.presentingViewController?.presentedViewController == navigationController {
                              return true
                          }
                      }
                  
                      return potentiallyPresentedViewController.tabBarController?.presentingViewController is UITabBarController
                  }
                  

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 2012-04-11
                    • 2019-01-24
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2021-11-14
                    • 1970-01-01
                    • 2012-01-04
                    相关资源
                    最近更新 更多