9

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?

Shaun Inman
  • 1,968
  • 1
  • 19
  • 28

6 Answers6

10

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;        

}
Community
  • 1
  • 1
Felipe Sabino
  • 17,825
  • 6
  • 78
  • 112
5

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

- (BOOL)isModalViewController
{
    return [self isBeingPresented];
}
NielsKoole
  • 296
  • 4
  • 11
4

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.

Shaun Inman
  • 1,968
  • 1
  • 19
  • 28
  • 3
    Casting to subclass. That seems awkward. Why subclass? Why not add a category to UITabBarController which contains isModal? – Erik Engheim Nov 01 '12 at 15:18
2

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.

chockenberry
  • 7,811
  • 5
  • 32
  • 41
  • I don't understand how either suggestion connects to my question (not saying they don't, I just don't understand). If a button in UIWindow > UITabBarController > UINavigationBarController > ACustomTableViewController presents the same UITabBarController hierarchy modally how does the root UITabBarController controller in UIWindow know what's going on? Regarding self.navigationController.viewControllers doesn't that just contain the hierarchy within the current navigationController? How does that help me within the modal view that's presenting a separate instance of the same UITabBarController? – Shaun Inman Dec 04 '10 at 22:11
  • When you're loading your UITabBarController, if it's the rootViewController of the current UIWindow, you don't put the Done button in the UI. Otherwise, it's going to be above that rootViewController (probably modal, but you should check to be sure.) You're thinking in terms of the current view hierarchy… – chockenberry Dec 04 '10 at 22:31
  • The other comment about iterating through view controllers was just to show you that you didn't need to check parent, etc. It doesn't help with the problem of knowing if you're modal or not. – chockenberry Dec 04 '10 at 22:35
1

There is a much easier way in these Swift days.

extension UIViewController {

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

}
Trenskow
  • 3,783
  • 1
  • 29
  • 35
1

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?

BoltClock
  • 700,868
  • 160
  • 1,392
  • 1,356
Russell Quinn
  • 1,330
  • 1
  • 11
  • 14
  • It knows it is presenting a UITabBarController but it doesn't know it is presenting the subclassed UITableViewController (that is presented by one of many intermediate UINavigationControllers presented by the UITabBarController) that actually needs to know if it is being presented modally or not. Wouldn't this approach require me to subclass all the otherwise stock intermediate view controllers to ensure that the isModal value gets to the necessary view? – Shaun Inman Dec 04 '10 at 21:56