在我的视图控制器代码中,我如何区分:
模态呈现
推送到导航堆栈
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];
我想我最好告诉我的视图控制器什么时候是模态的,而不是试图确定。
带着一粒盐,没有测试。
- (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;
}
在斯威夫特:
添加一个标志来测试它是否是类类型的模态:
// 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 isModal: Bool {}
return
语句中的最后一个 false
参数有什么作用?
您忽略了一种方法: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
中,这就是我要推动的。
p
,而不是 po
。 po
用于打印对象。
isBeingPresented
的文档 - 此方法仅在从 viewWillAppear: 和 viewDidAppear: 方法内部调用时返回 YES。
isBeingPresented
、isBeingDismissed
、isMovingFromParentViewController
和 isMovingToParentViewController
仅在 4 个 view[Will|Did][Disa|A]ppear
方法内有效。
Swift 5
这是解决先前答案中提到的问题的解决方案,如果推送 UIViewController
位于呈现的 UINavigationController
堆栈中,则 isModal()
返回 true
。
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 是否重要?
isModal
将返回 true
。这是故意的吗?
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
}
}
}
Swift 5. 干净简单。
if navigationController?.presentingViewController != nil {
// Navigation controller is being presented modally
}
斯威夫特 4
var isModal: Bool {
return presentingViewController != nil ||
navigationController?.presentingViewController?.presentedViewController === navigationController ||
tabBarController?.presentingViewController is UITabBarController
}
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。再次有更多优化的空间。请在评论部分讨论需要处理的案例。
正如这里的许多人所建议的那样,“检查”方法并不适用于所有情况,在我的项目中,我提出了手动管理它的解决方案。关键是,我们通常自己管理演示 - 这不是幕后发生的事情,我们必须反省。
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];
此外,如果上述属性等于 SSViewControllerPresentationMethodUnspecified
,我们可以在 DEViewController
中添加“检查”的后备:
- (BOOL)isViewControllerPushed
{
if (self.viewControllerPresentationMethod != SSViewControllerPresentationMethodUnspecified) {
return (BOOL)(self.viewControllerPresentationMethod == SSViewControllerPresentationMethodPush);
}
else {
// fallback to default determination method
return (BOOL)self.navigationController.viewControllers.count > 1;
}
}
假设您以模态方式呈现的所有视图控制器都包装在一个新的导航控制器中(无论如何您都应该这样做),您可以将此属性添加到您的 VC。
private var wasPushed: Bool {
guard let vc = navigationController?.viewControllers.first where vc == self else {
return true
}
return false
}
要检测您的控制器是否被推送,只需在您想要的任何地方使用以下代码:
if ([[[self.parentViewController childViewControllers] firstObject] isKindOfClass:[self class]]) {
// Not pushed
}
else {
// Pushed
}
我希望这段代码可以帮助任何人......
如果您使用的是 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;
}
}
if let navigationController = self.navigationController, navigationController.isBeingPresented {
// being presented
}else{
// being pushed
}
self.navigationController != nil
表示它位于导航堆栈中。
这个解决方案怎么样 - 在 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 { }
块中在语义上更好,因为它与拥有导航控制器的情况相反。
对于一些想知道如何告诉 ViewController 它正在呈现的人
如果 A
正在展示/推送 B
在B中定义一个枚举和属性 enum ViewPresentationStyle { case Push case Present } //并写入属性 var vcPresentationStyle : ViewPresentationStyle = .Push //默认值,考虑到B是被推送的 现在在A视图控制器中,告诉B是否正在呈现/pushed by assignment presentationStyle func presentBViewController() { let bViewController = B() bViewController.vcPresentationStyle = .Present //告诉 B 它正在呈现 self.presentViewController(bViewController, animated: true, completion: nil) } 在 B 视图中的用法控制器覆盖 func viewDidLoad() { super.viewDidLoad() if self.vcPresentationStyle == .Present { //正在呈现 } else { //正在推送 } }
id presentedController = self.navigationController.modalViewController;
if (presentedController) {
// Some view is Presented
} else {
// Some view is Pushed
}
这将让您知道 viewController 是呈现还是推送
presentingViewController
始终是YES
;没有帮助。UITabBarController
被设置为根时,presentingViewController
为推送的 VC 返回YES
。所以,不适合我的情况。