如何检查视图控制器是不是以模态方式呈现或推送到导航堆栈上?
Posted
技术标签:
【中文标题】如何检查视图控制器是不是以模态方式呈现或推送到导航堆栈上?【英文标题】:How to check if a view controller is presented modally or pushed on a navigation stack?如何检查视图控制器是否以模态方式呈现或推送到导航堆栈上? 【发布时间】:2014-06-30 11:30:15 【问题描述】:如何在我的视图控制器代码中区分:
模态呈现 推送到导航堆栈presentingViewController
和 isMovingToParentViewController
在这两种情况下都是 YES
,所以不是很有帮助。
使事情复杂化的是我的父视图控制器有时是模态的,要检查的视图控制器被推送到它上面。
原来我的问题是我将htmlViewController
嵌入到UINavigationController
中,然后呈现。这就是为什么我自己的尝试和下面的好答案都不起作用的原因。
HtmlViewController* termsViewController = [[HtmlViewController alloc] initWithDictionary:dictionary];
UINavigationController* modalViewController;
modalViewController = [[UINavigationController alloc] initWithRootViewController:termsViewController];
modalViewController.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentViewController:modalViewController
animated:YES
completion:nil];
我想我最好告诉我的视图控制器什么时候是模态的,而不是试图确定。
【问题讨论】:
【参考方案1】:持保留态度,未测试。
- (BOOL)isModal
if([self presentingViewController])
return YES;
if([[[self navigationController] presentingViewController] presentedViewController] == [self navigationController])
return YES;
if([[[self tabBarController] presentingViewController] isKindOfClass:[UITabBarController class]])
return YES;
return NO;
【讨论】:
我在另一个 SO 帖子中发现了这个。但是,如果推送的视图控制器的父级是模态的,则不起作用;这就是我遇到的情况。 正如我所写,presentingViewController
在我的情况下始终是YES
;没有帮助。
presentingViewController
返回 YES
推送的 VC,当有一个 UITabBarController
被设置为根。所以,不适合我的情况。
如果你呈现一个视图控制器然后它会推送另一个视图控制器,这不起作用。
“如果你呈现一个视图控制器然后它会推送另一个视图控制器,这不起作用”这不是本意,推送的视图控制器没有被呈现。【参考方案2】:
在 Swift 中:
添加一个标志来测试它是否是类类型的模态:
// MARK: - UIViewController implementation
extension UIViewController
var isModal: Bool
let presentingIsModal = presentingViewController != nil
let presentingIsNavigation = navigationController?.presentingViewController?.presentedViewController == navigationController
let presentingIsTabBar = tabBarController?.presentingViewController is UITabBarController
return presentingIsModal || presentingIsNavigation || presentingIsTabBar
【讨论】:
应该在 var 中更好,例如var isModal: Bool
@malinois 已更改
return
语句中的最后一个false
参数有什么作用?
你需要改变让presentingIsNavigation = navigationController?.presentingViewController?.presentedViewController == navigationController && navigationController != nil
Swift 5:presentingIsNavigation = true 如果 navigationController 为 nil【参考方案3】:
您忽略了一种方法:isBeingPresented
。
isBeingPresented
在呈现视图控制器时为真,在被推送时为假。
- (void)viewWillAppear:(BOOL)animated
[super viewWillAppear:animated];
if ([self isBeingPresented])
// being presented
else if ([self isMovingToParentViewController])
// being pushed
else
// simply showing again because another VC was dismissed
【讨论】:
发帖前我也试过了,还是不行,isBeingPresented
是NO
。但我现在明白了原因,我将呈现的视图控制器嵌入到 UINavigationController
中,这就是我要推动的。
您不能推送导航控制器。也许您的意思是您正在展示导航控制器。
@jowie 在打印原始值时使用p
,而不是po
。 po
用于打印对象。
isBeingPresented
的文档 - 此方法仅在从 viewWillAppear: 和 viewDidAppear: 方法内部调用时返回 YES。
@Terrence 似乎最新的文档没有显示该信息,但它曾经存在。 isBeingPresented
、isBeingDismissed
、isMovingFromParentViewController
和 isMovingToParentViewController
仅在 4 个 view[Will|Did][Disa|A]ppear
方法内有效。【参考方案4】:
斯威夫特 5
这是解决先前答案中提到的问题的解决方案,当isModal()
返回true
如果推送UIViewController
在呈现的UINavigationController
堆栈中。
extension UIViewController
var isModal: Bool
if let index = navigationController?.viewControllers.firstIndex(of: self), index > 0
return false
else if presentingViewController != nil
return true
else if navigationController?.presentingViewController?.presentedViewController == navigationController
return true
else if tabBarController?.presentingViewController is UITabBarController
return true
else
return false
到目前为止,它确实对我有用。 如果有一些优化,请分享。
【讨论】:
为什么要查看tabBarController?.presentingViewController is UITabBarController
? presentingViewController
是否也是 UITabBarController 有关系吗?
如果 navigationController 为 nil,isModal
将返回 true
。这是故意的吗?【参考方案5】:
self.navigationController != nil 表示它在导航中 堆栈。
为了处理当前视图控制器被推送而导航控制器模态显示的情况,我添加了一些代码行来检查当前视图控制器是否是导航堆栈中的根控制器。
extension UIViewController
var isModal: Bool
if let index = navigationController?.viewControllers.firstIndex(of: self), index > 0
return false
else if presentingViewController != nil
return true
else if let navigationController = navigationController, navigationController.presentingViewController?.presentedViewController == navigationController
return true
else if let tabBarController = tabBarController, tabBarController.presentingViewController is UITabBarController
return true
else
return false
【讨论】:
一般来说,当您以模态方式呈现时,您将 viewController 放在 navigationController 上并呈现它。如果是这种情况,您的陈述将是错误的,但是在代码上会处理这种情况。请改进你的答案:) 处理所有用例的好工作。可能有一些重构的空间,但仍然赞成!【参考方案6】:斯威夫特 4
var isModal: Bool
return presentingViewController != nil ||
navigationController?.presentingViewController?.presentedViewController === navigationController ||
tabBarController?.presentingViewController is UITabBarController
【讨论】:
Swift 4.2 / ios 12。仍然运行良好,但请注意 navigationController?.presentingViewController?.presentedViewController === navigationController 如果两者都为零(例如,如果你调用它一个尚未呈现的视图控制器)。【参考方案7】:Swift 5。简洁明了。
if navigationController.presentingViewController != nil
// Navigation controller is being presented modally
【讨论】:
【参考方案8】:Swift 5 这个方便的扩展处理的案例比以前的答案少。这些情况是VC(视图控制器)是应用程序窗口的根VC,VC作为子VC添加到父VC。只有当视图控制器以模态方式呈现时,它才会尝试返回 true。
extension UIViewController
/**
returns true only if the viewcontroller is presented.
*/
var isModal: Bool
if let index = navigationController?.viewControllers.firstIndex(of: self), index > 0
return false
else if presentingViewController != nil
if let parent = parent, !(parent is UINavigationController || parent is UITabBarController)
return false
return true
else if let navController = navigationController, navController.presentingViewController?.presentedViewController == navController
return true
else if tabBarController?.presentingViewController is UITabBarController
return true
return false
感谢Jonauz's answer。再次有更多优化的空间。需要处理的案例请在评论区讨论。
【讨论】:
【参考方案9】:正如这里的许多人所建议的那样,“检查”方法并不适用于所有情况,在我的项目中,我提出了手动管理该方法的解决方案。 关键是,我们通常自己管理演示 - 这不是幕后发生的事情,我们必须自省。
DEViewController.h
文件:
#import <UIKit/UIKit.h>
// it is a base class for all view controllers within a project
@interface DEViewController : UIViewController
// specify a way viewcontroller, is presented by another viewcontroller
// the presented view controller should manually assign the value to it
typedef NS_ENUM(NSUInteger, SSViewControllerPresentationMethod)
SSViewControllerPresentationMethodUnspecified = 0,
SSViewControllerPresentationMethodPush,
SSViewControllerPresentationMethodModal,
;
@property (nonatomic) SSViewControllerPresentationMethod viewControllerPresentationMethod;
// other properties/methods...
@end
现在可以这样管理演示文稿:
推送到导航堆栈:
// DETestViewController inherits from DEViewController
DETestViewController *vc = [DETestViewController new];
vc.viewControllerPresentationMethod = SSViewControllerPresentationMethodPush;
[self.navigationController pushViewController:vc animated:YES];
带有导航的模态显示:
DETestViewController *vc = [DETestViewController new];
vc.viewControllerPresentationMethod = SSViewControllerPresentationMethodModal;
UINavigationController *nav = [[UINavigationController alloc]
initWithRootViewController:vc];
[self presentViewController:nav animated:YES completion:nil];
模态呈现:
DETestViewController *vc = [DETestViewController new];
vc.viewControllerPresentationMethod = SSViewControllerPresentationMethodModal;
[self presentViewController:vc animated:YES completion:nil];
此外,在DEViewController
中,如果上述属性等于SSViewControllerPresentationMethodUnspecified
,我们可以添加“检查”的后备:
- (BOOL)isViewControllerPushed
if (self.viewControllerPresentationMethod != SSViewControllerPresentationMethodUnspecified)
return (BOOL)(self.viewControllerPresentationMethod == SSViewControllerPresentationMethodPush);
else
// fallback to default determination method
return (BOOL)self.navigationController.viewControllers.count > 1;
【讨论】:
【参考方案10】:假设您以模态方式呈现的所有 viewController 都包装在一个新的 navigationController 中(无论如何您都应该这样做),您可以将此属性添加到您的 VC。
private var wasPushed: Bool
guard let vc = navigationController?.viewControllers.first where vc == self else
return true
return false
【讨论】:
无论如何你都应该这样做 - 请解释为什么? 亚历山大,你不应该,真的。【参考方案11】:要检测您的控制器是否被推送,只需在您想要的任何地方使用以下代码:
if ([[[self.parentViewController childViewControllers] firstObject] isKindOfClass:[self class]])
// Not pushed
else
// Pushed
我希望这段代码可以帮助任何人...
【讨论】:
当你在多个地方使用同一个视图控制器类时,这个方法不起作用,因为它只检查它的类。您可以改为显式检查相等性。【参考方案12】:如果您使用的是 ios 5.0 或更高版本,请使用此代码
-(BOOL)isPresented
if ([self isBeingPresented])
// being presented
return YES;
else if ([self isMovingToParentViewController])
// being pushed
return NO;
else
// simply showing again because another VC was dismissed
return NO;
【讨论】:
【参考方案13】:if let navigationController = self.navigationController, navigationController.isBeingPresented
// being presented
else
// being pushed
【讨论】:
【参考方案14】:self.navigationController != nil
表示它在导航堆栈中。
【讨论】:
仍然可以在模态导航控制器中 所以'modal'和'pushed on navigation stack'并不是相互排斥的。认为这取决于上下文,但检查 self.navigationController 是否不为零确实可以回答它是否是导航控制器的视图控制器。 @Daniel 区别在于“推送”和“呈现”。 “模态”与它无关。当他们说“模态”时,我相信“ColdLogic”的意思是“呈现”。【参考方案15】:这个解决方案怎么样 - 在 iOS 15 和 Xcode 13.1 下测试:
var isPresented: Bool
if let nvc = navigationController
return nvc.viewControllers.firstIndex(of: self) == 0
else
return presentingViewController != nil
【讨论】:
感谢您的回答。我会试着找时间检查一下。顺便说一句,我认为将第二个return
语句放在 else
块中在语义上更好,因为它与拥有导航控制器的情况相反。【参考方案16】:
对于一些想知道如何告诉 ViewController 它正在呈现的人
如果A
正在展示/推送B
在B
中定义enum
和property
enum ViewPresentationStyle
case Push
case Present
//and write property
var vcPresentationStyle : ViewPresentationStyle = .Push //default value, considering that B is pushed
现在在A
视图控制器中,通过分配presentationStyle
告诉B
是否正在呈现/推送它
func presentBViewController()
let bViewController = B()
bViewController.vcPresentationStyle = .Present //telling B that it is being presented
self.presentViewController(bViewController, animated: true, completion: nil)
在B
视图控制器中的使用
override func viewDidLoad()
super.viewDidLoad()
if self.vcPresentationStyle == .Present
//is being presented
else
//is being pushed
【讨论】:
【参考方案17】:id presentedController = self.navigationController.modalViewController;
if (presentedController)
// Some view is Presented
else
// Some view is Pushed
这会让你知道 viewController 是否被展示或推送
【讨论】:
此属性已弃用。以上是关于如何检查视图控制器是不是以模态方式呈现或推送到导航堆栈上?的主要内容,如果未能解决你的问题,请参考以下文章
将 MFMailComposeViewController 推送到导航堆栈上?未以模态方式呈现
如何在保持导航栏存在的同时以模态方式呈现视图控制器。 (对于设置视图控制器)