1

I need to display a UIAlertController coming from the AppDelegate whenever I received a message from the topic.

I found this code on how to show a UIAlertController from the AppDelegate.

UIWindow* topWindow = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
    topWindow.rootViewController = [UIViewController new];
    topWindow.windowLevel = UIWindowLevelAlert + 1;

UIAlertController *uiAlert= ...

topWindow.hidden = YES;

It has no problem showing the alert on any view in my storyboard. The problem is that whenever I received multiple messages from the Topic, it also shows the UIAlertController multiple times, creating a layer of UIAlerController making the background black.

I tried this code to fix the problem, but it didn't

if (![topWindow.rootViewController.presentedViewController isKindOfClass:[UIAlertController class]]) {
            [topWindow makeKeyAndVisible];
            [topWindow.rootViewController presentViewController:uiAlert animated:YES completion:nil];
        }

What should be the condition to only present one UIAlertController if there's no current UIAlertController presented?

NSNoob
  • 5,548
  • 6
  • 41
  • 54
Jongers
  • 595
  • 1
  • 9
  • 29
  • Possible duplicate of [How to present UIAlertController when not in a view controller?](https://stackoverflow.com/questions/26554894/how-to-present-uialertcontroller-when-not-in-a-view-controller) – Sharda Prasad May 25 '17 at 06:45

2 Answers2

1
func checkIfAlertHasPresented() -> UIAlertController?
{
    if var topController = UIApplication.sharedApplication().keyWindow?.rootViewController
    {
        while let presentedVC = topController.presentedViewController
        {
            topController = presentedVC
        }
        if topController is UIAlertController
        {
            return (topController as! UIAlertController)
        }
        else
        {
            return nil
        }
    }
    return nil
}
Mohamed Raffi
  • 1,165
  • 1
  • 7
  • 15
1

I'd suggest not doing it this way, from the AppDelegate. There is absolutely no need to mess with the rootViewController.

Suggested Approaches

You can take two approaches for this:

  1. Create a BaseViewController, add the message showing method in it. Inherit all your ViewControllers from that BaseViewController and use the shared method.
  2. Create a utility class, Add a class method which shows message. Add that utility class either to your pch file so that it could be accessed everywhere or you could just import it where ever you want to display the message. Don't forget to add another Class method which gets the currently Visible viewcontrller.

First Approach: Using a BaseViewController (Recommended)

If you are using the BaseViewController Approach, your method will look like this:

-(void) showMessageWithHeader:(NSString *)header
                      andBody:(NSString *)bodyMessage{
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:header message:bodyMessage preferredStyle:UIAlertControllerStyleActionSheet];
    UIAlertAction * okAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
        
    }];
    [alertController addAction:okAction];
    [self presentViewController:alertController animated: YES completion: nil];
}

You can use it like the following in any ViewController inherited from your base:

[self showMessageWithHeader:@"Alert"
                    andBody:@"This message was generated from base class"];

I'd personally recommend this approach. You should always show your messages from ViewControllers, not let's say, some manager Class. You can return the message to ViewController using a block or whatever and then display it using this method.


Second Approach: Using a shared utility Class.

If you, however, prefer the utility class approach (For example if you insist on showing message directly from your manager Class which obviously doesn't know which ViewController is currently visible), add a utilityClass, let's suppose it's called UIUtilities. Now add two Class methods to it:

+ (UIViewController *) getVisibleViewController{
    UIViewController *topController = [UIApplication sharedApplication].keyWindow.rootViewController;
    topController = [UIUtilities getVisibleViewControllerFrom:topController];
    
    return topController;
}

+ (UIViewController *) getVisibleViewControllerFrom:(UIViewController *) vc {
    if ([vc isKindOfClass:[UINavigationController class]]) {
        return [UIUtilities getVisibleViewControllerFrom:[((UINavigationController *) vc) visibleViewController]];
    } else if ([vc isKindOfClass:[UITabBarController class]]) {
        return [UIUtilities getVisibleViewControllerFrom:[((UITabBarController *) vc) selectedViewController]];
    } else {
        if (vc.presentedViewController) {
            return [UIUtilities getVisibleViewControllerFrom:vc.presentedViewController];
        } else {
            return vc;
        }
    }
}

Using + (UIViewController *) getVisibleViewController will return you the currently visible ViewController. With that part done, you can now add your class method to show messages on VisibleViewController:

+(void) showMessageWithHeader:(NSString *)header
                      andBody:(NSString *)bodyMessage{
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:header message:bodyMessage preferredStyle:UIAlertControllerStyleActionSheet];
    UIAlertAction * okAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
        
    }];
    [alertController addAction:okAction];
    if(![[UIUtilities getVisibleViewController] isKindOfClass:[UIAlertController class]]){
             [[UIUtilities getVisibleViewController] presentViewController:alertController animated: YES completion: nil];
    }
    
}

Now you can call this method from where ever you want to show a message like:

[UIUtilities showMessageWithHeader:@"Alert"
                           andBody:@"This message came from Utility class"];

This will show only one Alert at one time. If one Alert is already visible, others won't be displayed. I wouldn't do it this way but since that's what you want, oh well.


Now no matter how many messages you receive, they will all stack up on currently visible ViewController and you can simply dismiss them one by one (Or only one Alert will be visible at one time as you require, depending on the approach you take), without any additional hassle.

Community
  • 1
  • 1
NSNoob
  • 5,548
  • 6
  • 41
  • 54
  • 1
    Wow this looks very promising. I already left from work but I will try this as soon as I arrive tmrw. I might go with the second approach since the message will come from a class without a view. – Jongers May 25 '17 at 09:02