【发布时间】:2011-02-17 10:12:18
【问题描述】:
是否可以在 ViewController 类内部检查它是否显示为模态视图控制器?
【问题讨论】:
标签: iphone ios view controller modal-dialog
是否可以在 ViewController 类内部检查它是否显示为模态视图控制器?
【问题讨论】:
标签: iphone ios view controller modal-dialog
由于 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 回答的提示。
【讨论】:
nil == nil返回YES,这不是我们想要的结果。
如果您正在寻找 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
【讨论】:
modalViewController 属性已被弃用。文档建议改用 presentedViewController。
NSLog(@"%@", self.navigationController.parentViewController) 打印 (null) - 你能解释一下原因吗?我的 ViewController 通过 storyboard 中的 navController 与模态视图控制器连接。
.parentViewController 已弃用,必须改用 .presentingViewController。
在 iOS5+ 中,正如您在 UIViewController Class Reference 中看到的,您可以从属性“presentingViewController”中获取它。
呈现视图控制器 呈现此视图控制器的视图控制器。 (只读)
@property(nonatomic, readonly) UIViewController *presentingViewController
讨论
如果收到此消息的视图控制器由另一个视图控制器呈现,则此属性包含正在呈现它的视图控制器。如果未显示视图控制器,但正在显示其祖先之一,则此属性保存显示最近祖先的视图控制器。如果既没有显示视图控制器也没有显示它的任何祖先,则此属性为 nil。
可用性
适用于 iOS 5.0 及更高版本。
声明于
UIViewController.h
【讨论】:
presentingViewController。它也可以在容器视图控制器中工作,因为它会自动遍历祖先。
如果没有,您可以在 UIViewController 子类中为此 (presentedAsModal) 定义一个属性,并将其设置为 YES,然后再将 ViewController 呈现为模态视图。
childVC.presentedAsModal = YES;
[parentVC presentModalViewController:childVC animated:YES];
您可以在 viewWillAppear 覆盖中检查此值。
我相信没有说明视图如何呈现的官方属性,但没有什么能阻止您创建自己的视图。
【讨论】:
UINavigationController 呈现为模态,则此解决方案不起作用...除非您创建自定义导航控制器只是为了添加此属性。之后,在控制器内部,每次您需要检查控制器是否显示为模态时,您都必须不断将 self.navigationController 转换为这个自定义类
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
【讨论】:
这应该可行。
if(self.parentViewController.modalViewController == self)…
【讨论】:
UINavigationController 和 UITabBarController 情况。到目前为止,它工作得很好
最好的检查方法
if (self.navigationController.presentingViewController) {
NSLog(@"Model Present");
}
【讨论】:
如果您不需要区分全屏模态视图和非模态视图,我的项目就是这种情况(我正在处理一个仅在表单和页面表中出现的问题),您可以使用 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;
}
【讨论】:
在 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
}
【讨论】:
在我的项目中,我有一个视图控制器(详细信息),可以由主视图控制器以模态方式(添加新项目时)或推送(编辑现有项目时)呈现。当用户点击 [完成] 时,详细视图控制器调用主视图控制器的方法来通知它已准备好关闭。 Master 必须确定 Detail 如何呈现才能知道如何关闭它。我就是这样做的:
UIViewController *vc = self.navigationController.viewControllers.lastObject;
if (vc == self) {
[self dismissViewControllerAnimated:YES completion:NULL];
} else {
[self.navigationController popViewControllerAnimated:YES];
}
【讨论】:
这样的 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
}
但是,我认为我之前的答案是一个更清洁的解决方案。
【讨论】:
对我有用的是:
// 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 上尝试。
【讨论】:
我已经四处寻找这个问题的正确答案,但我找不到任何涵盖所有可能情况的答案。我写了这几行似乎可以完成这项工作的代码。您可以找到一些内联 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;
}
希望对您有所帮助。
【讨论】:
这是我对@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
}
【讨论】: