Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UIStatusBar Appearance with Multiple Windows

First, my app's setup:

Most of the app's view controllers exist in your standard navigation controller hierarchy, but I also have a second window over the main app window, which hosts a view controller (NotificationVC). If NotificationVC is presenting a notification, it will change the status bar style to contrast with the notifications, but otherwise it defers the style to the main window's root view controller.

My issue is that changes in the main window that would normally trigger a status bar appearance update (pushing, popping or presenting a view controller, or calling -[UIViewController setNeedsStatusBarAppearanceUpdate]) have no effect.

Here's the relevant code from NotificationVC:

@implementation NotificationVC

- (UIStatusBarStyle)preferredStatusBarStyle
{
    if (self.isShowingNotification)
    {
        if (self.notificationView.hasDarkBackground)
        {
            return UIStatusBarStyleLightContent;
        }
        else
        {
            return UIStatusBarStyleDefault;
        }
    }
    else
    {
        return [[UIApplication sharedApplication].delegate window].rootViewController.preferredStatusBarStyle;
    }
}

@end

How can I get the status bar to update from one of the view controllers in the main window?

Note: Manually setting the status bar appearance (-[UIApplication setStatusBarStyle:]) is not an acceptable solution for this app.

like image 446
Austin Avatar asked May 28 '14 15:05

Austin


1 Answers

This seems to be an optimization on UIViewController's part - it won't trigger an update if it isn't in the top window.

I was able to work around the issue by swizzling out UIViewController's implementation of setNeedsStatusAppearanceUpdate for one that is aware of the notification view controller's window.

@interface UIViewController (NotificationWindow)

@end

@implementation UIViewController (NotificationWindow)

+ (void)load
{
    Method original = class_getInstanceMethod([UIViewController class], @selector(setNeedsStatusBarAppearanceUpdate));
    Method swizzled = class_getInstanceMethod([UIViewController class], @selector(swiz_setNeedsStatusBarAppearanceUpdate));
    method_exchangeImplementations(original, swizzled);
}

- (void)swiz_setNeedsStatusBarAppearanceUpdate
{
    UIWindow *topWindow = [UIApplication sharedApplication].windows.lastObject;

    if (![self.view.window isEqual:topWindow] && [topWindow.rootViewController isKindOfClass:[NotificationVC class]])
    {
        [[[UIApplication sharedApplication].windows.lastObject rootViewController] swiz_setNeedsStatusBarAppearanceUpdate];
    }
    else
    {
        [self swiz_setNeedsStatusBarAppearanceUpdate];
    }
}

@end

It's a little hacky — I try to avoid swizzling — but it allows the status bar appearance API to work as-is, without requiring every other view controller to be aware of NotificationVC.

like image 109
Austin Avatar answered Nov 06 '22 21:11

Austin