如何检查视图控制器是否以模态方式显示或推送到导航堆栈上?

在我的视图控制器代码中,如何区分:

  • 以模态方式呈现
  • 推送到导航栈上

在这两种情况下,presentingViewControllerisMovingToParentViewController都是 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];

我想我最好告诉我的视图控制器什么时候是模态的,而不是试图确定。

145132 次浏览

self.navigationController != 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;
}

你忽略了一个方法: isBeingPresented

当呈现视图控制器时,isBeingPresented为 true; 当推送视图控制器时,isBeingPresented为 false。

- (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
}
}

斯威夫特:

通过类类型添加一个标志来测试它是否是一个模态:

// 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
}
}
id presentedController = self.navigationController.modalViewController;
if (presentedController) {
// Some view is Presented
} else {
// Some view is Pushed
}

这将让您知道 viewController 是显示还是推送

正如这里的许多人所建议的,“检查”方法并不适用于所有情况,在我的项目中,我提出了手动管理的解决方案。 重点是,我们通常自己管理展示——这不是幕后发生的事情,我们必须反省。

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;
}
}

控制器! = 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 都包装在一个新的导航控制器中(无论如何您都应该这样做) ,您可以将这个属性添加到 VC 中。

private var wasPushed: Bool {
guard let vc = navigationController?.viewControllers.first where vc == self else {
return true
}


return false
}

如果有人想知道,如何告诉 ViewController 它正在呈现

如果 A呈现/推动 B

  1. B中定义 enumproperty

    enum ViewPresentationStyle {
    case Push
    case Present
    }
    
    
    //and write property
    
    
    var vcPresentationStyle : ViewPresentationStyle = .Push //default value, considering that B is pushed
    
  2. Now in A view controller, tell B if it is being presented/pushed by assigning presentationStyle

    func presentBViewController() {
    let bViewController = B()
    bViewController.vcPresentationStyle = .Present //telling B that it is being presented
    self.presentViewController(bViewController, animated: true, completion: nil)
    }
    
  3. Usage in B View Controller

    override func viewDidLoad() {
    super.viewDidLoad()
    
    
    if self.vcPresentationStyle == .Present {
    //is being presented
    }
    else {
    //is being pushed
    }
    
    
    }
    

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
}
}
}

到目前为止,我觉得还不错。 如果有一些优化,请分享。

如果您正在使用 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;
}
}

Swift 4

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

检测控制器是否被推动或者不仅仅是在你想要的任何地方使用下面的代码:

if ([[[self.parentViewController childViewControllers] firstObject] isKindOfClass:[self class]]) {


// Not pushed
}
else {


// Pushed
}

我希望这个密码能帮助任何人。

if let navigationController = self.navigationController, navigationController.isBeingPresented {
// being presented
}else{
// being pushed
}

简洁明了。

if navigationController?.presentingViewController != nil {
// Navigation controller is being presented modally
}

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
}
}

多亏了 琼斯的回答。同样,还有进行更多优化的空间。请在评论部分讨论需要处理的案例。

这个在 iOS 15和 Xcode 13.1下测试过的解决方案怎么样:

var isPresented: Bool {
if let nvc = navigationController {
return nvc.viewControllers.firstIndex(of: self) == 0
} else {
return presentingViewController != nil
}
}