如何检查视图控制器是模态显示还是被推入导航堆栈?


126

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

  • 模态呈现
  • 推入导航堆栈

这两个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];

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

Answers:


125

拿一粒盐,没有测试。

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

12
我在另一个SO帖子中找到了这个。但是,如果推送视图控制器的父级是模式的,则不起作用;这就是我遇到的情况。
含义-

2
如我所写,presentingViewController总是YES以我为例;没有帮助。
含义-2014年

3
presentingViewControllerYESUITabBarController设置为根时,返回推送的VC 。所以,不适合我的情况。
Yevhen Dubinin 2014年

5
如果您提供一个视图控制器,则该按钮将推入另一个,这将不起作用。

3
“如果您提供一个视图控制器,那么它会推送另一个,这是行不通的。”这不是我们的意图,不会显示被推送的视图控制器。
科林·斯威林

87

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

4
应该在var中更好,例如var isModal: Bool {}
malinois

1
@malinois已更改
YannSteph

语句中的最后一个false参数return做什么?
damjandd

您需要更改以让presentingIsNavigation = navigationController?.presentingViewController?.presentedViewController == navigationController && navigationController!= nil
famfamfam

78

您忽略了一种方法: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
    }
}

2
我想这太发布之前,它不工作,isBeingPresentedNO。但是我现在知道了原因,我将呈现的视图控制器嵌入到UINavigationController,这就是我要推送的那个。
含义-2014年

1
您不能按导航控制器。也许您的意思是要介绍导航控制器。
rmaddy

3
@jowie使用p,而不是po在打印原始值时使用。po用于打印对象。
rmaddy

37
-的文档isBeingPresented-仅当从viewWillAppear:和viewDidAppear:方法内部调用时,此方法才返回YES。
funct7

4
@Terrence似乎最新的文档没有显示该信息,但是它曾经在那里。的isBeingPresentedisBeingDismissedisMovingFromParentViewControllerisMovingToParentViewController仅4点中有效view[Will|Did][Disa|A]ppear的方法。
rmaddy

29

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

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


为什么需要检查tabBarController?.presentingViewController is UITabBarController ?这是否presentingViewController也是UITabBarController也有关系吗?
Hlung

并且如果navigationController为nil,isModal将返回true。这是故意的吗?
Hlung

28

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上并进行呈现。如果是这种情况,您的陈述将是错误的,但是在代码上会处理这种情况。请改善您的回答:)
E-Riddie '17

处理所有用例的好工作。可能有一些重构空间,但仍然值得赞扬!!
让·雷蒙德·达赫

12

斯威夫特4

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

Swift 4.2 / iOS 12仍然可以正常工作,但是要注意,如果两个都为nil,则navigationController?.presentingViewController?.presentedViewController === navigationController的计算结果为true(例如,如果在尚未使用的视图控制器上调用它呈现)。
伊莱·伯克


3

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

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

3

假定您以模态形式显示的所有viewController都包装在新的navigationController中(无论如何您都应该总是这样做),则可以将此属性添加到VC中。

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

    return false
}

1
无论如何,您应该始终执行该操作 -请说明原因?
亚历山大·阿巴库莫夫

亚历山大,你不应该,真的。
nickdnk

2

要检测是否推送了控制器,请仅在所需的任何位置使用以下代码:

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

    // Not pushed
}
else {

    // Pushed
}

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


1
当您在多个地方使用相同的视图控制器类时,此方法将不起作用,因为它只会检查该类的类。您可以改为显式检查相等性。
gklka '19

1

self.navigationController != nil 表示它在导航堆栈中。


25
仍然可以在模式导航控制器中使用
ColdLogic

因此,“模态”和“推入导航堆栈”不是互斥的。认为这取决于上下文,但是检查self.navigationController是否不为nil确实会回答它是否是导航控制器的视图控制器。
丹尼尔(Daniel)

@Daniel区别在于“推送”和“呈现”之间。“模态”与它无关。我相信当他们说“模态”时,“ ColdLogic”的意思是“呈现”
。– rmaddy 2014年

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

0

如果您使用的是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;
    }
}

0

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

谢乔纳兹的回答。同样,还有进行更多优化的空间。请在评论部分讨论需要处理的案件。


-1

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

如果A正在呈现/推动B

  1. enumproperty中定义B

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

    func presentBViewController() {
        let bViewController = B()
        bViewController.vcPresentationStyle = .Present //telling B that it is being presented
        self.presentViewController(bViewController, animated: true, completion: nil)
    }
  3. BView Controller中的用法

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

-2
id presentedController = self.navigationController.modalViewController;
if (presentedController) {
     // Some view is Presented
} else {
     // Some view is Pushed
}

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


4
不推荐使用此属性。
Morkrom 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.