15

I want to display a alert message from viewDidLoad() method of ViewController.m instead from viewDidAppear() method.

Here is my code :

- (void)viewDidLoad {
    [super viewDidLoad];

    //A SIMPLE ALERT DIALOG
    UIAlertController *alert =   [UIAlertController
                              alertControllerWithTitle:@"My Title"
                              message:@"Enter User Credentials"
                              preferredStyle:UIAlertControllerStyleAlert];


    UIAlertAction *cancelAction = [UIAlertAction
                               actionWithTitle:NSLocalizedString(@"Cancel", @"Cancel action")
                               style:UIAlertActionStyleCancel
                               handler:^(UIAlertAction *action)
                               {
                                   NSLog(@"Cancel action");
                               }];

    UIAlertAction *okAction = [UIAlertAction
                           actionWithTitle:NSLocalizedString(@"OK", @"OK action")
                           style:UIAlertActionStyleDefault
                           handler:^(UIAlertAction *action)
                           {
                               NSLog(@"OK action");
                           }];

    [alert addAction:cancelAction];
    [alert addAction:okAction];
    [self presentViewController:alert animated:YES completion:nil];
}

and I am getting this error:

Warning: Attempt to present <UIAlertController: 0x7fbc58448960> on <ViewController: 0x7fbc585a09d0> whose view is not in the window hierarchy!

ipinak
  • 5,739
  • 3
  • 23
  • 41
Shivani Arorra
  • 161
  • 1
  • 1
  • 5

4 Answers4

26

OK not a bug, the issue is that in viewDidLoad the view hierarchy is not fully set. If you use viewDidAppear, then the hierarchy is set.

If you really want to call this alert in viewDidLoad you can do so by wrapping your presentation call in this GCD block to cause a slight delay, waiting for the next run loop, however I suggest you don't (it's ugly).

dispatch_async(dispatch_get_main_queue(), ^ {
    [self presentViewController:alert animated:YES completion:nil];
});
Woodstock
  • 22,184
  • 15
  • 80
  • 118
  • 1
    my pleasure, just a note, that although the above works, I guess Apple didn't intent the alerts to be presented in viewDidLoad, because it's very early in the viewController lifecycle. – Woodstock Jan 09 '16 at 10:45
  • Great answer Woodstock! Thank you for the feedback saving my day :) – sugarakis Feb 10 '21 at 00:01
10

to move this call to the viewDidAppear: method.

Akash
  • 461
  • 2
  • 14
0

You have to embed a navigation controller and present the controller

- (void)viewDidLoad {
  [super viewDidLoad];

  //A SIMPLE ALERT DIALOG


   UIAlertController *alert =   [UIAlertController
                          alertControllerWithTitle:@"My Title"
                          message:@"Enter User Credentials"
                          preferredStyle:UIAlertControllerStyleAlert];


   UIAlertAction *cancelAction = [UIAlertAction
                           actionWithTitle:NSLocalizedString(@"Cancel", @"Cancel action")
                           style:UIAlertActionStyleCancel
                           handler:^(UIAlertAction *action)
                           {
                               NSLog(@"Cancel action");
                           }];

   UIAlertAction *okAction = [UIAlertAction
                       actionWithTitle:NSLocalizedString(@"OK", @"OK action")
                       style:UIAlertActionStyleDefault
                       handler:^(UIAlertAction *action)
                       {
                           NSLog(@"OK action");
                       }];

   [alert addAction:cancelAction];
   [alert addAction:okAction];

   [self.navigationController presentViewController:alert animated:NO completion:nil];
   //    [self presentViewController:cameraView animated:NO completion:nil]; //this will cause view is not in the window hierarchy error

}

OR

  [self.view addSubview:alert.view];
  [self addChildViewController:alert];
  [alert didMoveToParentViewController:self];
Mayank Patel
  • 3,868
  • 10
  • 36
  • 59
  • i try your suggested code. but app is crashed when I click on any button i.e OK , Cancel.. and also Alert Box is displayed on top-left side – Shivani Arorra Jan 09 '16 at 10:37
  • @ShivaniArorra i have updated my ans check this you need to do `[self.navigationController presentViewController:alert animated:NO completion:nil];` – Mayank Patel Jan 09 '16 at 10:43
-1

Swift 3 iOS 10, I used operation queue to put the block of code that updates the UI onto the main thread.

import UIKit

class ViewController2: UIViewController {

var opQueue = OperationQueue()

override func viewDidLoad() {
    super.viewDidLoad()
        let alert = UIAlertController(title: "MESSAGE", message: "HELLO WORLD!", preferredStyle: UIAlertControllerStyle.alert)
        // add an action (button, we can add more than 1 buttons)
        alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: nil))
        // show the alert
        self.opQueue.addOperation {
            // Put queue to the main thread which will update the UI
            OperationQueue.main.addOperation({
                self.present(alert, animated: true, completion: nil)
            })
        }
    }
}

In short we are using async. This allows the alert message to be displayed as expected (even when we are in viewDidLoad()).

coarist
  • 789
  • 9
  • 11
  • This is actually adding a block to a local operation queue that adds another block to the main operation queue that then presents the alert. It's more normal to use a dispatch queue for this: `DispatchQueue.main.async { ... }`. – Craig McMahon Apr 21 '17 at 13:08