Prevent iOS in-call status bar from pushing Phonegap UIWebView offscreen

My issue is this: whenever an iPhone user is in call, or is using his or her phone as a hotspot, the iOS 7 status bar is enlarged, thus pushing my Phonegap application’s UIWebView off the bottom of the screen. The enlarged status bar is termed the “in-call status bar”. See below image:

Wonky in-call status bar.

  • Carthage: no shared framework schemes for iOS platform (for my own framework)
  • iOS Google Places API: “This IP, site or mobile application is not authorized to use this API key”
  • Intercepting pan gestures over a UIScrollView breaks scrolling
  • How to make Siri launch app on specific keyword?
  • How to remove provisioning profiles from Xcode
  • Xcode 6 process launch failed: timed out trying to launch app
  • Stack Overflow answers I have tried to remedy this:

    Iphone- How to resize view when call status bar is toggled?

    How In-Call status bar impacts UIViewController's view size ? (and how to handle it properly)

    Additionally, there does not seem to be any sort of event fired by Phonegap that informs me of the status bar’s change. Listening to the Phonegap “pause” event is useless, as 1) it’s known to have quirks in iOS and 2) it doesn’t really cover the hotspot case.

    My Objective-C skills are very minimal, and I only resort to asking this sort of question after putting in the requisite 4+ hours Googling, Stack Overflowing, wailing, etc…

    Gods of Stack Overflow, render unto me thine bounteous nerd fury.

    2 Solutions Collect From Internet About “Prevent iOS in-call status bar from pushing Phonegap UIWebView offscreen”

    Came up with the following solution based on Jef’s suggestions. What you’ll want to do is the following:

    • Observe the native didChangeStatusBarFrame delegate
    • Get size information about the statusbar via native statusBarFrame
    • Expose information to your webview by triggering an event that passes it

    I have setup a Github repo with all the code you find in this answer.

    Setup notification in AppDelegate

    // Appdelegate.m
    - (void)application:(UIApplication *)application didChangeStatusBarFrame:(CGRect)oldStatusBarFrame
    {
      NSMutableDictionary *statusBarChangeInfo = [[NSMutableDictionary alloc] init];
      [statusBarChangeInfo setObject:@"statusbarchange" 
                         forKey:@"frame"];
    
      [[NSNotificationCenter defaultCenter] postNotificationName:@"statusbarchange" 
                                            object:self 
                                            userInfo:statusBarChangeInfo];
     }
    

    Make statusBarChange selector available

    // MainViewController.h
    @protocol StatusBarChange <NSObject>
        -(void)onStatusbarChange:(NSNotification*)notification;
    @end
    

    Setup the listener. This gets the origin and size dictionaries from statusBarFrame whenever it changes and fires an event in the webview passing along this data.

    // MainViewController.m
    - (void)onStatusbarChange:(NSNotification*)notification
    {
        // Native code for
        NSMutableDictionary *eventInfo = [self getStatusBarInfo];
        [self notifiy:notification.name withInfo:eventInfo];
    }
    
    - (void)notifiy:(NSString*)event withInfo:(NSMutableDictionary*)info
    {
        NSString *json = [self toJSON:info];
        NSString *cmd = [NSString stringWithFormat:@"cordova.fireWindowEvent('\%@\', %@)", event, json];
        [self.webView stringByEvaluatingJavaScriptFromString:cmd];
    }
    
    - (NSMutableDictionary *)getStatusBarInfo
    {
        CGRect statusBarFrame = [[UIApplication sharedApplication] statusBarFrame];
    
        NSMutableDictionary *statusBarInfo = [[NSMutableDictionary alloc] init];
        NSMutableDictionary *size = [[NSMutableDictionary alloc] init];
        NSMutableDictionary *origin = [[NSMutableDictionary alloc] init];
    
        size[@"height"] = [NSNumber numberWithInteger:((int) statusBarFrame.size.height)];
        size[@"width"] = [NSNumber numberWithInteger:((int) statusBarFrame.size.width)];
    
        origin[@"x"] = [NSNumber numberWithInteger:((int) statusBarFrame.origin.x)];
        origin[@"y"] = [NSNumber numberWithInteger:((int) statusBarFrame.origin.y)];
    
        statusBarInfo[@"size"] = size;
        statusBarInfo[@"origin"] = origin;
    
        return statusBarInfo;
    }
    
    - (NSString *) toJSON:(NSDictionary *)dictionary {
        NSError *error;
        NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dictionary options:NSJSONWritingPrettyPrinted error:&error];
        return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
    }
    
    - (void)dealloc
    {
        [[NSNotificationCenter defaultCenter] removeObserver:self];
    }
    

    All this allows you to listen for window.statusbarchange event, e.g. like this:

    // www/js/index.js
    window.addEventListener('statusbarchange', function(e){
      // Use e.size.height to adapt to the changing status bar      
    
    }, false)
    

    I’d say that this always happens when you return from background, no?

    In others words is it possible for the bar to enlarge without your app being at least briefly pushed to the background by incoming call etc?

    If so surely you can query the status bar height in your delegates -(void)will(orDid)resume and adjust accordingly?

    If it does happen without leaving the foreground that make a little more difficult, we’ll need to work out which notifications to observe for, I know there’s an audioSession interruption notif in the case of incoming calls, not sure about the hotspot thing but surely there is a notification for that too..

    Edit ok here they are, choose one of these notifications to observe..

    UIApplicationWillChangeStatusBarFrameNotification
    
    UIApplicationDidChangeStatusBarFrameNotification
    

    Or implement one of these callbacks in your delegate

    -application:willChangeStatusBarFrame:
    
    -application:didChangeStatusBarFrame: