9
votes

My application's main window contains a xib-based UITabBarController (fully configured in Interface Builder) that can also be presented modally (much like the Music.app "Add songs to playlist" modal view). The UITabBarController contains a number of UINavigationControllers which in turn contain subclassed UITableViewControllers. This is how I'm currently detecting if the subclassed UITableViewController is being presented inside a modal UITabBarController:

- (void)viewDidLoad {
    [super viewDidLoad];

    self.isModal = NO;

    UIViewController *child     = self;
    UIViewController *parent    = self.parentViewController;
    while (parent) {
        if (parent.modalViewController && parent.modalViewController == child) {
            self.isModal = YES;
            break;
        }
        child   = parent;
        parent  = parent.parentViewController;
    }

    if (self.isModal) {
        // modal additions, eg. Done button, navigationItem.prompt
    }
    else {
        // normal additions, eg. Now Playing button
    }
}

Is there a way to do this that doesn't involve walking up the parentViewController tree or subclassing all the intermediate view controllers to pass down the isModal state when they are initialized?

6

6 Answers

10
votes

If you a looking for iOS 6+, this answer is deprecated and you should check Gabriele Petronella's answer


I answered a very similar question a while ago, where I have a function to determine whether the current controller is presented as modal or not, without the need to subclass the tab bar controller here:

Is it possible to determine whether ViewController is presented as Modal?

At the original answer there are some basic explanations on how this function works and you cancheck there if needed, but here it is

-(BOOL)isModal {

     BOOL isModal = ((self.parentViewController && self.parentViewController.modalViewController == self) || 
            //or if I have a navigation controller, check if its parent modal view controller is self navigation controller
            ( self.navigationController && self.navigationController.parentViewController && self.navigationController.parentViewController.modalViewController == self.navigationController) || 
            //or if the parent of my UITabBarController is also a UITabBarController class, then there is no way to do that, except by using a modal presentation
            [[[self tabBarController] parentViewController] isKindOfClass:[UITabBarController class]]);

    //iOS 5+
    if (!isModal && [self respondsToSelector:@selector(presentingViewController)]) {

        isModal = ((self.presentingViewController && self.presentingViewController.modalViewController == self) || 
             //or if I have a navigation controller, check if its parent modal view controller is self navigation controller
             (self.navigationController && self.navigationController.presentingViewController && self.navigationController.presentingViewController.modalViewController == self.navigationController) || 
             //or if the parent of my UITabBarController is also a UITabBarController class, then there is no way to do that, except by using a modal presentation
             [[[self tabBarController] presentingViewController] isKindOfClass:[UITabBarController class]]);

    }

    return isModal;        

}
5
votes

Since iOS5 you can also use isBeingPresented on a viewController instance:

- (BOOL)isModalViewController
{
    return [self isBeingPresented];
}
4
votes

Got an answer on Twitter. I ended up subclassing UITabBarController and adding a BOOL isModal instance property which is set to YES when presenting modally. Then subviews can use self.tabBarController with a cast to the subclass to access the isModal property and render/behave accordingly.

2
votes

I'd look at getting the root view controller and checking if it has a modal view controller. You can get that view controller from UIWindow. Note also that you can iterate through the current view controller's hierarchy using UINavigationController's viewControllers property: for (UIViewController *viewController in self.navigationController.viewControllers) { ... } is faster and simpler.

1
votes

You could set the display state in a custom initialiser when you present the view. I mean the code presenting it will know how it's being presented, right?

- (void)initInModalMode:(BOOL)isModal

It's better than having the view retroactively discover its status later on?

0
votes

There is a much easier way in these Swift days.

extension UIViewController {

    var isPresentedModally: Bool {
        return presentingViewController?.presentedViewController == self || parent?.isPresentedModally == true
    }

}