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

Posted

技术标签:

【中文标题】如何检查视图控制器是不是以模态方式呈现或推送到导航堆栈上?【英文标题】:How to check if a view controller is presented modally or pushed on a navigation stack?如何检查视图控制器是否以模态方式呈现或推送到导航堆栈上? 【发布时间】:2014-06-30 11:30:15 【问题描述】:

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

模态呈现 推送到导航堆栈

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

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

【问题讨论】:

【参考方案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
    

【讨论】:

发帖前我也试过了,还是不行,isBeingPresentedNO。但我现在明白了原因,我将呈现的视图控制器嵌入到 UINavigationController 中,这就是我要推动的。 您不能推送导航控制器。也许您的意思是您正在展示导航控制器。 @jowie 在打印原始值时使用p,而不是popo 用于打印对象。 isBeingPresented 的文档 - 此方法仅在从 viewWillAppear: 和 viewDidAppear: 方法内部调用时返回 YES。 @Terrence 似乎最新的文档没有显示该信息,但它曾经存在。 isBeingPresentedisBeingDismissedisMovingFromParentViewControllerisMovingToParentViewController 仅在 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 中定义enumproperty

    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 推送到导航堆栈上?未以模态方式呈现

如何在保持导航栏存在的同时以模态方式呈现视图控制器。 (对于设置视图控制器)

在 UIButton 单击时以编程方式创建 Segue 或推送到另一个视图控制器

嵌入到导航控制器中的动画根视图控制器以模态方式呈现

在导航堆栈中以模态方式呈现视图控制器