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

How to check if a view controller is presented modally or pushed on a navigation stack?

提问人:meaning-matters 提问时间:5/13/2014 最后编辑:craftmeaning-matters 更新时间:3/17/2022 访问量:159485

问:

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

  • 以模式呈现
  • 推送导航堆栈

在这两种情况下都是,所以不是很有帮助。presentingViewControllerisMovingToParentViewControllerYES

更复杂的是,我的父视图控制器有时是模态的,要检查的视图控制器被推送到该控制器上。

事实证明,我的问题是我将我的嵌入到然后呈现的中。这就是为什么我自己的尝试和下面的好答案没有奏效的原因。HtmlViewControllerUINavigationController

HtmlViewController*     termsViewController = [[HtmlViewController alloc] initWithDictionary:dictionary];
UINavigationController* modalViewController;

modalViewController = [[UINavigationController alloc] initWithRootViewController:termsViewController];
modalViewController.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentViewController:modalViewController
                   animated:YES
                 completion:nil];

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

iOS Objective-C Swift UIVieviceController UINuageController

评论


答:

-1赞 Daniel 5/13/2014 #1

self.navigationController != nil这意味着它位于导航堆栈中。

评论

27赞 ColdLogic 5/13/2014
仍然可以在模态导航控制器中
0赞 rmaddy 5/13/2014
@Daniel 区别在于“推送”和“呈现”。“模态”与它无关。我相信当他们说“模态”时,“ColdLogic”的意思是“呈现”。
138赞 ColdLogic 5/13/2014 #2

用一粒盐服用,没有测试。

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

评论

4赞 Yevhen Dubinin 6/11/2014
presentingViewController当被设置为根时,返回推送的 VC。所以,不适合我的情况。YESUITabBarController
94赞 rmaddy 5/13/2014 #3

您忽略了一种方法:.isBeingPresented

isBeingPresented在显示视图控制器时为 true,在推送时为 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
    }
}

评论

3赞 meaning-matters 5/13/2014
我在发布之前也尝试过这个,但它不起作用,是.但我现在明白了原因,我正在将我呈现的视图控制器嵌入到一个 中,这就是我正在推动的那个。isBeingPresentedNOUINavigationController
1赞 rmaddy 5/13/2014
无法推送导航控制器。也许您的意思是您正在展示导航控制器。
49赞 funct7 6/25/2015
文档 - 仅当从 viewWillAppear: 和 viewDidAppear: 方法内部调用时,此方法才返回 YES。isBeingPresented
0赞 meaning-matters 7/18/2016
是的,我的意思是我展示了 .UINavigationController
5赞 rmaddy 1/28/2017
@Terrence 似乎最新的文档没有显示这些信息,但它曾经在那里。、 和 仅在 4 种方法中有效。isBeingPresentedisBeingDismissedisMovingFromParentViewControllerisMovingToParentViewControllerview[Will|Did][Disa|A]ppear
111赞 King-Wizard 12/5/2014 #4

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

评论

2赞 famfamfam 7/23/2020
您需要更改以让 presentingIsNavigation = navigationController?。presentingViewController?。presentedViewController == navigationController && navigationController != nil
0赞 Tà Truhoada 6/6/2021
Swift 5:如果 navigationController 为 nil,则 presentingIsNavigation = true
-3赞 iCoder86 5/5/2015 #5
id presentedController = self.navigationController.modalViewController;
if (presentedController) {
     // Some view is Presented
} else {
     // Some view is Pushed
}

这将使您知道 viewController 是否显示或推送

评论

5赞 Morkrom 6/20/2015
此属性已弃用。
3赞 Yevhen Dubinin 6/23/2015 #6

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

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

此外,如果上述属性等于:DEViewControllerSSViewControllerPresentationMethodUnspecified

- (BOOL)isViewControllerPushed
{
    if (self.viewControllerPresentationMethod != SSViewControllerPresentationMethodUnspecified) {
        return (BOOL)(self.viewControllerPresentationMethod == SSViewControllerPresentationMethodPush);
    }

    else {
        // fallback to default determination method
        return (BOOL)self.navigationController.viewControllers.count > 1;
    }
}
35赞 Jibeex 6/15/2016 #7

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

评论

2赞 E-Riddie 8/29/2017
一般来说,当你以模式方式呈现时,你把 viewController 放在 navigationController 上,然后你就呈现它了。如果是这样的话,你的陈述就是错误的,但是在代码上处理了这种情况。请改进您的回答:)
4赞 Senõr Ganso 9/7/2016 #8

假设您以模式方式呈现的所有 viewController 都包装在一个新的 navigationController 中(无论如何您都应该这样做),您可以将此属性添加到您的 VC 中。

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

    return false
}

评论

3赞 Alexander Abakumov 4/5/2017
无论如何,您都应该这样做 - 请解释为什么?
0赞 Saif 1/13/2017 #9

对于一些想知道的人,如何告诉 ViewController 它正在呈现

如果正在演示/推送AB

  1. 定义一个 和 inenumpropertyB

    enum ViewPresentationStyle {
        case Push
        case Present
    }
    
    //and write property 
    
    var vcPresentationStyle : ViewPresentationStyle = .Push //default value, considering that B is pushed 
    
  2. 现在在视图控制器中,通过分配来判断它是否正在呈现/推送ABpresentationStyle

    func presentBViewController() {
        let bViewController = B()
        bViewController.vcPresentationStyle = .Present //telling B that it is being presented
        self.presentViewController(bViewController, animated: true, completion: nil)
    }
    
  3. 在视图控制器中的使用B

    override func viewDidLoad() {
        super.viewDidLoad()
    
        if self.vcPresentationStyle == .Present {
            //is being presented 
        }
        else {
            //is being pushed
        }
    
    }
    
45赞 Jonauz 3/26/2017 #10

Swift 5
这是一个解决方案,它解决了前面答案中提到的问题,当推送的返回在呈现的堆栈中时。
isModal()trueUIViewControllerUINavigationController

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

到目前为止,它确实对我有用。 如果有一些优化,请分享。

评论

0赞 Hlung 4/21/2020
为什么需要检查?这是否也是 UITabBarController?tabBarController?.presentingViewController is UITabBarController presentingViewController
1赞 Hlung 4/21/2020
如果navigationController为nil,将返回.这是故意的吗?isModaltrue
2赞 Shahbaz Abbasi 4/3/2017 #11

如果您使用的是 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;
    }
}
12赞 Charlton Provatas 11/18/2017 #12

斯威夫特 4

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

评论

0赞 Eli Burke 10/13/2018
斯威夫特 4.2 / iOS 12。仍然运行良好,但请注意 navigationController?.presentingViewController?。presentedViewController === 如果两者都为 nil,则 navigationController 的计算结果为 true(例如,如果您在尚未显示的视图控制器上调用它)。
2赞 Arash Zeinoddini 3/6/2018 #13

要检测您的控制器是否被推送,只需在您想要的任何位置使用以下代码:

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

    // Not pushed
}
else {

    // Pushed
}

我希望这段代码可以帮助任何人......

评论

1赞 gklka 2/13/2019
当您在多个位置使用相同的视图控制器类时,此方法不起作用,因为它只检查它的类。您可以改为显式检查相等性。
1赞 mkto 6/5/2018 #14
if let navigationController = self.navigationController, navigationController.isBeingPresented {
    // being presented
}else{
    // being pushed
}
21赞 Kirill 7/31/2019 #15

斯威夫特 5.干净简单。

if navigationController?.presentingViewController != nil {
    // Navigation controller is being presented modally
}
9赞 Mehedi Hasan 3/9/2020 #16

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 的回答。同样,还有更多优化的空间。请在评论部分讨论需要处理的案例。

评论

0赞 Michał Ziobro 12/13/2022
不适用于 SwiftUI 案例。如果通过 UIViewContorllerRepresentable 在没有导航视图的情况下显示 swiftUI 视图,则存在父 UIHostingControll<BridgedNavigationView.RootView>或事件
0赞 blackjacx 11/10/2021 #17

这个解决方案 - 在 iOS 15 和 Xcode 13.1 下测试:

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