确定ViewController是否以模态方式呈现,是否可能?

119

在ViewController类内部,有没有可能检查它是作为模态视图控制器呈现的?

14个回答

98

自从iOS 6中modalViewController被弃用后,这里提供了一个适用于iOS 5+的版本,并且可以编译而不会产生警告。

Objective-C:

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

Swift:

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

向Felipe的答案致敬。


2
很好的发现,我长时间没用它,注意到它已经被废弃了...我编辑了我的答案,这样人们在使用iOS 6+时就可以从这里找到正确的代码了,谢谢。 - Felipe Sabino
10
如果父视图控制器是一个模态视图控制器,而我们的视图控制器被推送到其上,则无法正常工作。 - meaning-matters
2
有一个错误,我们应该检查两边是否都为nil,因为nil == nil返回YES,这不是我们想要的结果。 - CocoaBob
1
@GabrielePetronella,您介意我更新答案并包括该方法的Swift实现吗? - Michael Waterfall
1
@MichaelWaterfall,非常感谢您的帮助。 - Gabriele Petronella
显示剩余2条评论

78

如果你正在寻找iOS 6+的解决方案,那么这个答案已经过时了,你应该查看Gabriele Petronella的答案


没有简便的方法可以在UIKit原生属性或方法中实现这一点。您可以检查控制器的几个方面,以确保它作为模态呈现。

因此,为了检查当前(以下代码中表示为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:我已经创建了一个Gist备用,以防万一https://gist.github.com/3174081


请注意,modalViewController属性已在iOS 6中被弃用。文档建议使用presentedViewController替代。 - Bart Jacobs
NSLog(@"%@", self.navigationController.parentViewController) 打印出 (null) - 你能解释一下为什么吗?我的 ViewController 通过 storyboard 中的 navController 与模态视图控制器相连接。 - Roman
@oyatek,你可以使用Pastebin或类似的工具展示一些代码吗? - Felipe Sabino
@Feilpe 我找到了问题 - .parentViewController 已经被弃用,必须使用 .presentingViewController 代替。 - Roman
@oyatek 我不明白...这就是为什么我加入了iOS 5+回退和presentingViewController逻辑...难道它在你遇到的某些特定情况下无法工作吗? - Felipe Sabino
显示剩余2条评论

35

iOS5+ 中,可以从属性 "presentingViewController" 获取“UIViewController Class Reference”中所述的内容。详情请参见 UIViewController 类的参考资料

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

@property(nonatomic, readonly) UIViewController *presentingViewController
Discussion

如果接收此消息的视图控制器由另一个视图控制器呈现,则此属性保存呈现它的视图控制器。如果当前视图控制器未被呈现,但其祖先之一正在被呈现,则此属性保存最近的祖先视图控制器。如果既未呈现当前视图控制器也未呈现任何祖先视图控制器,则此属性保存 nil。

Availability
iOS 5.0 及更高版本可用。
Declared In
UIViewController.h


3
完美运行,使用if (self.presentingViewController) {//这是一个模态视图控制器} else {//这是一个普通的视图控制器}。 - mashdup
2
在我看来,这是这里唯一正确的答案。只需检查是否存在“presentingViewController”。它也适用于容器视图控制器,因为它会自动遍历祖先。 - Daniel Rinser

17
如果没有的话,您可以在您的UIViewController子类中定义一个属性(presentedAsModal),并在将ViewController呈现为模态视图之前将其设置为YES。
childVC.presentedAsModal = YES;
[parentVC presentModalViewController:childVC animated:YES];

您可以在viewWillAppear重写方法中检查此值。

我相信没有官方属性来表示视图的呈现方式,但是没有任何阻止您创建自己的属性。


没错,这就是我所做的,但我正在寻找其他更好的解决方案。谢谢。 - lukewar
如果您将UINavigationController作为模态呈现,则此解决方案无效...除非您创建一个自定义导航控制器来添加此属性。之后,在控制器内部,每次需要检查控制器是否以模态方式呈现时,都必须将self.navigationController强制转换为此自定义类。 - Felipe Sabino

8
Petronella的答案在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为nil :(。 - lukewar
如果您只获取 'self.parentViewController',它是否返回正确的父对象? - kubi
4
可能的问题是你的UIViewController子类位于UINavigationController或UITabBarController(或两者都在)中,因此你可能需要深入查看视图层次结构,以找到作为模态视图控制器呈现的父级。 - hpique
@hgpc 我在我的项目中需要这个检查,所以我添加了一个答案来检查UINavigationControllerUITabBarController两种情况。目前它运行得非常好。 - Felipe Sabino

5

检查的最佳方法

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

2
在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而没有任何模态呈现。 - mariusLAN
1
第一个if语句覆盖了第二个if语句中的所有内容,使第二个语句变得多余。 我不确定这里的意图是什么。 - isoiphone

2

如果您不需要区分全屏模态视图和非模态视图,这在我的项目中是适用的(我正在处理一个仅在表单表和页面表中发生的问题),您可以使用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;
}

1
在我的项目中,我有一个视图控制器(Detail),可以通过主视图控制器以模态方式呈现(添加新项时)或推入方式呈现(编辑现有项时)。当用户点击[完成]时,Detail视图控制器调用Master视图控制器的方法通知它已准备好关闭。主控制器必须确定如何呈现细节,以便知道如何关闭它。这是我的做法:
UIViewController *vc = self.navigationController.viewControllers.lastObject;
if (vc == self) {
    [self dismissViewControllerAnimated:YES completion:NULL];
} else {
    [self.navigationController popViewControllerAnimated:YES];
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接