How to change the status bar background color and text color on iOS 13?
Asked Answered
I

16

87

With the arrival of iOS 13 statusBar's view is no longer accessible trough:

value(forKey: "statusBar") as? UIView

Due to:

Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'App called -statusBar or -statusBarWindow on UIApplication: this code must be changed as there's no longer a status bar or status bar window. Use the statusBarManager object on the window scene instead.'

But it's not clear how it should be used for changing colours as keyWindow?.windowScene?.statusBarManager does not appear to contain anything related to it.

I'm compiling my code with (iOS 10, *) compatibility, so I intend to continue using UIKit.

Any ideas regarding this subject?

Isotherm answered 18/6, 2019 at 14:18 Comment(7)
Why are you trying to manually change the status bar background color? By default it will match the color of your app.Benefactor
It's a legacy app that includes a custom status bar colour making it look apart from the rest of the appIsotherm
There has never been a valid way to modify the status bar color. Such solutions always end up breaking eventually. Never dig into the private subview structure.Benefactor
@HugoAlonso see my answer here: #56556754Toshikotoss
@Benefactor can you add an answer stating the fact that is not possible and what's the best approach so I can set it as the accepted one?Isotherm
youtube.com/watch?v=bZXk0Dwxopw.... refer this videoDagnah
@hugoAlonso You can check this answer: https://mcmap.net/q/55604/-how-can-i-change-status-bar-alpha-in-ios-13Deterge
H
56

You can add some conditions or use first one. Just create some extension for UIApplication.

extension UIApplication {
var statusBarUIView: UIView? {
    if #available(iOS 13.0, *) {
        let tag = 38482
        let keyWindow = UIApplication.shared.windows.filter {$0.isKeyWindow}.first

        if let statusBar = keyWindow?.viewWithTag(tag) {
            return statusBar
        } else {
            guard let statusBarFrame = keyWindow?.windowScene?.statusBarManager?.statusBarFrame else { return nil }
            let statusBarView = UIView(frame: statusBarFrame)
            statusBarView.tag = tag
            keyWindow?.addSubview(statusBarView)
            return statusBarView
        }
    } else if responds(to: Selector(("statusBar"))) {
        return value(forKey: "statusBar") as? UIView
    } else {
        return nil
    }
  }
}

UPDATED: Sorry, I don't have enough time to test it in real projects, but it works in "Hello world" app. You can read more info about keyWindow and statusBarFrame in order to make it better.

extension UIApplication {
var statusBarUIView: UIView? {

    if #available(iOS 13.0, *) {
        let tag = 3848245

        let keyWindow = UIApplication.shared.connectedScenes
            .map({$0 as? UIWindowScene})
            .compactMap({$0})
            .first?.windows.first

        if let statusBar = keyWindow?.viewWithTag(tag) {
            return statusBar
        } else {
            let height = keyWindow?.windowScene?.statusBarManager?.statusBarFrame ?? .zero
            let statusBarView = UIView(frame: height)
            statusBarView.tag = tag
            statusBarView.layer.zPosition = 999999

            keyWindow?.addSubview(statusBarView)
            return statusBarView
        }

    } else {

        if responds(to: Selector(("statusBar"))) {
            return value(forKey: "statusBar") as? UIView
        }
    }
    return nil
  }
}
Halstead answered 7/8, 2019 at 12:41 Comment(14)
keyWindow is deprecated in iOS 13.Benefactor
Why would you want to use something that's deprecated?Isotherm
@Halstead I was using the iOS version with np, updated to Xcode 11 and iOS 13 and got a crash, I checnged the extension code to your answer and no more crashes, thanks. But can you give an example on how to use your code information iOS 12 and iOS 13. Right now it just shows what to add in the extensionPollster
as pointed by @rmaddy, keyWindow as well as statusBarFrame are both deprecatedExeunt
new to iOS dev. Can anyone tell me where to use this code snippet? in AppDelegate or viewController?Selfpropulsion
@HugoAlonso, There are no warnings in my projects, maybe the reason is their age.Halstead
@Selfpropulsion in my case i use it in viewController to have ability to change the statusBar colour for different VC.Halstead
@LanceSamaria to change statusBar colour write this in some place of your code. (For example in viewWillAppear or in AppDelegate): UIApplication.shared.statusBarUIView?.backgroundColor = .yellowHalstead
@Halstead thanks for getting back to me. I figured out how to use it later that day after I asked you. I forgot what I did exactly but maybe it was what you just wrote? Any much appreciated. Happy Coding!Pollster
Where is stated that the view is the one with tag 3848245?Isotherm
@HugoAlonso, it works for me. The tag for each subview is 0 by default. But maybe in some case the window can already has a subview with same tag. If you want to be sure that your tag is unique you can do the next: 1) create function that get UIView description and convert it to Int hash value, than use it like a tag to identify your view. 2) create function that generate random integer value and check if there are the same in window, if yes - generate new random value, no - use it like a tag. 3) try to find some docs where is stated that your tag is unique.Halstead
@isHidden, how can I apply it to swiftUIFreeliving
@Skysoft13 it's completely different story for SwiftUI. Here's a solution I use #62207772Refine
'windows' was deprecated in iOS 15.0: Use UIWindowScene.windows on a relevant window scene insteadPrecipitate
T
55

Unfortunately Apple deprecated some of the mentioned methods of accessing the status bar and editing its attributes. You will have to use the StatusBarManager object of the WindowScene. The following method works for iOS 13 and above:

extension UINavigationController {

    func setStatusBar(backgroundColor: UIColor) {
        let statusBarFrame: CGRect
        if #available(iOS 13.0, *) {
            statusBarFrame = view.window?.windowScene?.statusBarManager?.statusBarFrame ?? CGRect.zero
        } else {
            statusBarFrame = UIApplication.shared.statusBarFrame
        }
        let statusBarView = UIView(frame: statusBarFrame)
        statusBarView.backgroundColor = backgroundColor
        view.addSubview(statusBarView)
    }

}
Trucking answered 28/11, 2019 at 11:33 Comment(6)
Best answer of allBischoff
Perfect Solution!Lakes
How about the check if the statusBar view was added already? This is to make sure we do not add same subview again and again.Blackdamp
the problem with this solution is that statusBarFrame is nil in case you call it in viewDidLoad or viewWillAppear. Works only in viewDidAppear what does not sound as a best place to set up UIPoleyn
If you are unsure how to call this like I was, in viewDidAppear (thanks @Artiom) call self.navigationController?.setStatusBar(backgroundColor: yourColor) AND self.navigationController?.navigationBar.setNeedsLayout()Townley
Alternatively, you can call self.navigationController?.setStatusBar(backgroundColor: yourColor) in viewWillAppearTownley
E
8

I have encountered this issue before. My application got crash while I run this code using XCode 11 and Swift 5.0.

Previous Code:-

UIApplication.shared.statusBarView?.backgroundColor = UIColor.init(red: 243/250, green: 243/250, blue: 243/250, alpha: 1)

Just Changed to:-

if #available(iOS 13.0, *) {
    let statusBar = UIView(frame: UIApplication.shared.keyWindow?.windowScene?.statusBarManager?.statusBarFrame ?? CGRect.zero)
     statusBar.backgroundColor = UIColor.init(red: 243/250, green: 243/250, blue: 243/250, alpha: 1)
     UIApplication.shared.keyWindow?.addSubview(statusBar)
} else {
     UIApplication.shared.statusBarView?.backgroundColor = UIColor.init(red: 243/250, green: 243/250, blue: 243/250, alpha: 1)
}

Now my problem solved. Happy coding.

Eigenvalue answered 15/10, 2019 at 13:43 Comment(4)
keyWindow is deprecated in iOS 13.Benefactor
let keyWindow = UIApplication.shared.connectedScenes .filter({$0.activationState == .foregroundActive}) .map({$0 as? UIWindowScene}) .compactMap({$0}) .first?.windows .filter({$0.isKeyWindow}).first You can call directly as:- keyWindow?.addSubview(statusBar)Eigenvalue
Don't put code in a comment, update your answer as needed. Keep in mind that there may be two active scenes so simply grabbing the first could be wrong.Benefactor
For keyWindows: UIApplication.shared.windows.filter {$0.isKeyWindow}.firstHealth
C
7

for swift 5.0 I've done this to change background color,

    if #available(iOS 13.0, *) {
           let window = UIApplication.shared.windows.filter {$0.isKeyWindow}.first 
           // Reference - https://mcmap.net/q/55605/-how-to-resolve-39-keywindow-39-was-deprecated-in-ios-13-0
           let statusBar = UIView(frame: window?.windowScene?.statusBarManager?.statusBarFrame ?? CGRect.zero)
           statusBar.backgroundColor = .white
           window?.addSubview(statusBar)
    } else {
           UIApplication.shared.statusBarView?.backgroundColor = .white
           UIApplication.shared.statusBarStyle = .lightContent
    }

https://medium.com/@trivediniki94/surprises-after-upgrading-to-xcode-11-ios-13-b52b36e05fa8

Cupping answered 11/10, 2019 at 7:56 Comment(9)
That's better, but it still isn't ideal. What happens when the app is showing two different scenes on an iPad? This code only shows the extra view above one of the scenes and not both.Benefactor
@Benefactor So should i put a for loop, and which would execute the same code for all windows ?Cupping
Any chance of someone providing an Objective C version of this?Balcer
Value of type '[UIWindow]' has no member 'addSubview'Kabuki
@Daniella I've updated my code, would you please check your code now!Cupping
@jayantrawat yes, In didFinishLaunchingWithOptions application method within APPDELEGATE.Cupping
@Niki its not working for me with Xcode 11/iOS13 & UIApplication.shared.statusBarView is also not available in Xcode11...Mousterian
This works for me but... (I am making black status bar with white text, working without a NavController). I found that in my first viewController I had to put the code from this answer in viewDidAppear for it to work. In subsequent viewControllers in the flow I put it in viewWillAppear. I am also using this solution - which works for a non-NavController scenario: https://mcmap.net/q/53070/-how-to-change-status-bar-text-color-in-ios. That makes the statusbar text white. Also not using the pre-OS13 partYourself
"Value of type 'UIApplication' has no member 'statusBarView'" error occured for Swift 5.Sartorial
B
6

This worked for me in Swift 5

   override func viewDidLoad() {
      super.viewDidLoad()

      if #available(iOS 13, *)
      {
          let statusBar = UIView(frame: (UIApplication.shared.keyWindow?.windowScene?.statusBarManager?.statusBarFrame)!)
          statusBar.backgroundColor = #colorLiteral(red: 0.2346, green: 0.3456, blue: 0.5677, alpha: 1)
          UIApplication.shared.keyWindow?.addSubview(statusBar)
      } else {
         // ADD THE STATUS BAR AND SET A CUSTOM COLOR
         let statusBar: UIView = UIApplication.shared.value(forKey: "statusBar") as! UIView
         if statusBar.responds(to:#selector(setter: UIView.backgroundColor)) {
            statusBar.backgroundColor = #colorLiteral(red: 0.2346, green: 0.3456, blue: 0.5677, alpha: 1)
         }
         UIApplication.shared.statusBarStyle = .lightContent
      }
   }
Bunchy answered 15/12, 2019 at 18:57 Comment(1)
I'm getting a warning of : 'keyWindow' was deprecated in iOS 13.0: Should not be used for applications that support multiple scenes as it returns a key window across all connected scenesCordiacordial
T
4

Use the following code:

if (@available(iOS 13, *)) {
    let statusBar1 =  UIView()
    statusBar1.frame = UIApplication.shared.statusBarFrame
    statusBar1.backgroundColor = UIColor.red
    UIApplication.shared.keyWindow?.addSubview(statusBar1)
}

to achieve this result:

enter image description here

Telugu answered 20/12, 2019 at 7:44 Comment(1)
'statusBarFrame' was deprecated in iOS 13.0: Use the statusBarManager property of the window scene instead. 'keyWindow' was deprecated in iOS 13.0: Should not be used for applications that support multiple scenes as it returns a key window across all connected scenes...Loreenlorelei
A
4

Tested and 100% worked for me

func statusBarColorChange(){

    if #available(iOS 13.0, *) {

            let statusBar = UIView(frame: UIApplication.shared.keyWindow?.windowScene?.statusBarManager?.statusBarFrame ?? CGRect.zero)
            statusBar.backgroundColor = AppThemeColor
            statusBar.tag = 100
            UIApplication.shared.keyWindow?.addSubview(statusBar)

    } else {

            let statusBar = UIApplication.shared.value(forKeyPath: "statusBarWindow.statusBar") as? UIView
            statusBar?.backgroundColor = AppThemeColor

        }
    }
}

remove status bar from the key window

func removeStatusBar(){

    if #available(iOS 13.0, *) {

        UIApplication.shared.keyWindow?.viewWithTag(100)?.removeFromSuperview()

    }
}

in viewDidLoad and viewWillAppear call above function

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

    removeStatusBar()
    statusBarColorChange()
}

override func viewDidLoad() {
    super.viewDidLoad()

    removeStatusBar()
    statusBarColorChange()

}
Atom answered 31/12, 2019 at 6:23 Comment(0)
C
4

KeyWindow is deprecated in iOS13. You can use this extension for swift 5 and iOS 13 to the top

extension UIApplication {

    var statusBarUIView: UIView? {

        if #available(iOS 13.0, *) {
            let tag = 3848245

            let keyWindow: UIWindow? = UIApplication.shared.windows.filter {$0.isKeyWindow}.first

            if let statusBar = keyWindow?.viewWithTag(tag) {
                return statusBar
            } else {
                let height = keyWindow?.windowScene?.statusBarManager?.statusBarFrame ?? .zero
                let statusBarView = UIView(frame: height)
                statusBarView.tag = tag
                statusBarView.layer.zPosition = 999999

                keyWindow?.addSubview(statusBarView)
                return statusBarView
            }

        } else {

            if responds(to: Selector(("statusBar"))) {
                return value(forKey: "statusBar") as? UIView
            }
        }
        return nil
      }
}

And use it in your didFinishLaunchingWithOptions in AppDelegate class like :

UIApplication.shared.statusBarUIView?.backgroundColor = .red(any color)
Cl answered 8/11, 2020 at 6:44 Comment(3)
How about notch device?Childress
It works on all kind of device even notch device.Cl
this is not working.Booted
A
3
if (@available(iOS 13, *))
{    
    UIView *statusBar = [[UIView alloc]initWithFrame:[UIApplication sharedApplication].keyWindow.windowScene.statusBarManager.statusBarFrame] ;
    statusBar.backgroundColor = [UIColor redColor];
    [[UIApplication sharedApplication].keyWindow addSubview:statusBar];

 }
Appropriation answered 20/9, 2019 at 12:44 Comment(1)
keyWindow is deprecated in iOS 13.Benefactor
G
2

you can try this

if (@available(iOS 13, *))
{
    UIView *_localStatusBar = [[UIApplication sharedApplication].keyWindow.windowScene.statusBarManager performSelector:@selector(createLocalStatusBar)];
    statusBar = [_localStatusBar performSelector:@selector(statusBar)];
}
else
{
    statusBar = [[UIApplication sharedApplication] valueForKey:@"statusBar"];
}
Gurkha answered 4/7, 2019 at 12:8 Comment(2)
keyWindow is deprecated in iOS 13.Benefactor
can you please add methods - createLocalStatusBar and @selector(statusBar)Alton
A
0
Use this Extension:

extension UINavigationController {

func setStatusBar(backgroundColor: UIColor) {
    let statusBarFrame: CGRect
    if #available(iOS 13.0, *) {
        statusBarFrame = view.window?.windowScene?.statusBarManager?.statusBarFrame ?? CGRect.zero
    } else {
        statusBarFrame = UIApplication.shared.statusBarFrame
    }
    let statusBarView = UIView(frame: statusBarFrame)
    statusBarView.backgroundColor = backgroundColor
    view.addSubview(statusBarView)
}}
Acetate answered 31/5, 2021 at 10:14 Comment(0)
P
0

If you want to make it for all your app, you can add to you Info.plist these keys and values ​​for them

View controller-based status bar appearance - NO
Status bar style - Light Content

enter image description here

Paucker answered 24/11, 2022 at 4:50 Comment(1)
This messes up SwiftUI a little. .statusBar(hidden: true) no longer works. If UIViewControllerBasedStatusBarAppearance is YES then SwiftUI can hide the status bar but can't have a light/white appearance.Incompressible
R
-1

this is an ObjC version of most voted answer for those like me who are still using it:

create a category of UIApplication and add it to your project:

@implementation UIApplication (iOS13PorcoDiDio)

- (UIView*) statusBar
{

    if([UIDevice getOSVersion] >= 13)
    {

        const NSUInteger k_TAG_STATUSBAR = 38482458385;

        UIView * vStatusBar = [[UIApplication sharedApplication].keyWindow viewWithTag:k_TAG_STATUSBAR];
        if(vStatusBar != nil)

            return vStatusBar;

        else {

            UIView *vStatusBar = [[UIView alloc] initWithFrame:[UIApplication sharedApplication].statusBarFrame];
            [vStatusBar setTag:k_TAG_STATUSBAR];
            [[UIApplication sharedApplication].keyWindow addSubview:vStatusBar];

            return vStatusBar;

        }

    } else if([UIApplication respondsToSelector:@selector(statusBar)])

        return (UIView*)[UIApplication sharedApplication].statusBar;

    else

        return nil;


}

@end
Raeleneraf answered 14/10, 2019 at 13:8 Comment(2)
keyWindow is deprecated in iOS 13.Benefactor
PorcoDiDio is deprecated in iOS 13Olinger
V
-1

This is the best answer I have ever seen.. Cheers

    if #available(iOS 13.0, *) {
    let app = UIApplication.shared
    let statusBarHeight: CGFloat = app.statusBarFrame.size.height

    let statusbarView = UIView()
        statusbarView.backgroundColor = ColorPalette.grayChateau  
    view.addSubview(statusbarView)

    statusbarView.translatesAutoresizingMaskIntoConstraints = false
    statusbarView.heightAnchor
        .constraint(equalToConstant: statusBarHeight).isActive = true
    statusbarView.widthAnchor
        .constraint(equalTo: view.widthAnchor, multiplier: 1.0).isActive = true
    statusbarView.topAnchor
        .constraint(equalTo: view.topAnchor).isActive = true
    statusbarView.centerXAnchor
        .constraint(equalTo: view.centerXAnchor).isActive = true

} else {
    let statusBar = UIApplication.shared.value(forKeyPath: "statusBarWindow.statusBar") as? UIView
    statusBar?.backgroundColor = UIColor.red
 }
Veliger answered 30/10, 2019 at 6:53 Comment(1)
'statusBarFrame' was deprecated in iOS 13.0: Use the statusBarManager property of the window scene instead.Kabuki
S
-1

I think the simplest way is to use NavigationController instead of ViewController. Changing of Navigation Bar background using storyboard will also reflect on status bar

NavigationController

Skillless answered 30/11, 2020 at 7:14 Comment(1)
It would be great to see which settings in the attribute inspector were changed, cause when I changed the title background in NavigationController it didn't affect the status bar backgroundRefine
H
-2

I do this, can get statusBar,but set statusBar backgroundColor do not work

UIView *statusBar;
if (@available(iOS 13, *)) {
    UIView *_localStatusBar = [[UIApplication sharedApplication].keyWindow.windowScene.statusBarManager performSelector:@selector(createLocalStatusBar)];
    statusBar = [_localStatusBar performSelector:@selector(statusBar)];
}
else {
    statusBar = [[UIApplication sharedApplication] valueForKey:@"statusBar"];
}
if ([statusBar respondsToSelector:@selector(setBackgroundColor:)]) {
    statusBar.backgroundColor = [UIColor redColor];
}
Hephzibah answered 26/8, 2019 at 13:11 Comment(3)
keyWindow is deprecated in iOS 13.Benefactor
So,how to get statusBarView in OCHephzibah
There is no statusBarView to get. There is no public API for it. In iOS 13 you can get the statusBarManager from a window scene but there is no view.Benefactor

© 2022 - 2024 — McMap. All rights reserved.