36

I'm trying to make a modal view controller in my app delegate (I created a function called showLoginView). But whenever I try to call it I get a warning in XCode:

Warning: Attempt to present <PSLoginViewController: 0x1fda2b40> on <PSViewController: 0x1fda0720> whose view is not in the window hierarchy!

Here's the method code:

- (void)showLoginView
{
    PSLoginViewController *loginViewController = [[UIStoryboard storyboardWithName:@"MainStoryboard" bundle:NULL] instantiateViewControllerWithIdentifier:@"PSLoginViewController"];
    [self.window.rootViewController presentViewController:loginViewController animated:NO completion:nil];
}

How can I add the view to the window hierarchy? Or maybe I'm doing something very wrong?

paulmelnikow
  • 16,895
  • 8
  • 63
  • 114
patryk
  • 642
  • 1
  • 9
  • 18

7 Answers7

31

You can't display a modal view controller from the appDelegate. You need to display a modal ViewController from whichever viewController is currently displaying full-screen. In other words, you need to put that code into your root view controller, or whichever one you want to display the modal vc from...

Also, you'll want to use the method "presentModalViewController" to present the modal. You can set properties on the modal vc such as:

vC.modalPresentationStyle = UIModalPresentationFormSheet;
vC.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentModalViewController:vC animated:YES];
HackyStack
  • 4,887
  • 3
  • 22
  • 28
  • but the rootViewController isn't the one displayed at the moment? How should I implement generic function, that whenever user credentials check fails it can present a login view controller? – patryk Nov 12 '12 at 20:26
  • Are you using a navigation controller? – HackyStack Nov 12 '12 at 20:29
  • 6
    I would use a navigation controller and then you can get a reference to the displayed one like: displayedController = [navController topViewController]; Then you can push modally like above where you would replace self with displayedController. – HackyStack Nov 12 '12 at 20:31
  • 16
    I fixed this problem by taking out the present method of the viewDidLoad and put it in the viewDidAppear. – daihovey Sep 25 '13 at 04:07
  • 8
    This solution is outdated: 'presentModalViewController:animated:' is deprecated: first deprecated in iOS 6.0. – acoustic May 19 '14 at 02:28
21

You can actually present a modal view Controller from the AppDelegate as long as you detect the current visible viewController and take care of the case where you current controller is a navigationController.

Here is what I do:

UIViewController *activeController = [UIApplication sharedApplication].keyWindow.rootViewController;
if ([activeController isKindOfClass:[UINavigationController class]]) {
   activeController = [(UINavigationController*) activeController visibleViewController];
}
[activeController presentModalViewController:loginViewController animated:YES];
Erwan
  • 3,733
  • 30
  • 25
  • 1
    Additionally you can exchange: `[activeController presentModalViewController:loginViewController animated:YES]` to `[activeController presentViewController:loginNavigationController animated:NO completion:nil]` if you want to present a navigation controller. – Matheus Abreu May 18 '14 at 23:09
  • @MatheusAbreu I am using this code to present my navigation controller from NSobject class which is called for logout functionality either from a ViewController or another NSObject class when a session is expired. This code works fine in first run. In second run it give me warning as, "Attempt to present on whose view is not in the window hierarchy!". I am unable to understand why it's changing the rootviewcontroller the second time. – Azhar Bandri Aug 22 '14 at 06:02
  • Now presentModalViewController is depricated. – biloshkurskyi.ss Dec 18 '15 at 11:05
8
UIViewController *activeController = [UIApplication sharedApplication].keyWindow.rootViewController;
if ([activeController isKindOfClass:[UINavigationController class]])
{
   activeController = [(UINavigationController*) activeController visibleViewController];
}
else if (activeController.modalViewController)
{
    activeController = activeController.modalViewController;
}
[activeController presentModalViewController:vc animated:YES];
SFeng
  • 2,226
  • 1
  • 19
  • 16
  • .modalViewController is now deprecated, you could use now activeController.presentedViewController – Pach Apr 20 '15 at 07:11
7

I ran into this problem on iOS 7 - the key to making any of the proposed solutions work was to call

[self.window makeKeyAndVisible];

in your AppDelegate. After that call, presenting a modal view from the window's rootViewController worked.

David Ganster
  • 1,985
  • 1
  • 18
  • 26
6

Another reason for that warning can be that you want to present a view controller from an instance which is not the top most view controller.

So first you have to get the topmost UIViewController and using this instance to call presentViewController:

UIViewController *root = [UIApplication sharedApplication].keyWindow.rootViewController;
while (root.presentedViewController) {
    root = root.presentedViewController;
}
Titus T
  • 116
  • 2
  • 8
  • 1
    Swift 3 UIApplication.shared.keyWindow?.rootViewController?.present(vc, animated: true, completion: nil) – Jason Oct 19 '16 at 02:56
3

You can NSLog(@"%@", self.window.rootViewController), and see what the rootViewController really is.

I came into this problem, when the rootViewController is a normal UIViewController. Replace it with a UINavigationController, wish it will help.

Sanbrother
  • 601
  • 5
  • 12
0

Faced this issue while trying to present controller from the call of delegate of other controller . i.e : show search filter with delegate , once done back to my controller and receive data via the delegate then present controller , all I had to do is to dispatch the present code cause while in a delegate you're in another thread , that's why you're presenting on your view from main thread another controller from that other thread , so have to go back to main thread , just put the presenting code like this :

     dispatch_async(dispatch_get_main_queue(), ^{
[self presentViewController:searchVC animated:true completion:nil];
 });

Hope this helps !

IsPha
  • 389
  • 4
  • 9