43
votes

In iOS 13 UIApplication.shared.statusBarFrame.height warns

'statusBarFrame' was deprecated in iOS 13.0: Use the statusBarManager property of the window scene instead.

How do you get the status bar height without using a deprecated API in iOS 13?

7
@rmaddy actually, you need it for a lot of things. Also, it is not your task to question functionalities of apps you don't have any background information about.linus_hologram
My query was meant to get more info so better answers could be provided. Knowing why might give people a chance to offer a better solution. Sometimes people do things incorrectly for the wrong reasons.rmaddy

7 Answers

70
votes

As the warning hints, you can access the statusBarManager which has a statusBarFrame property. This is defined on your UIWindow's windowScene.

let height = view.window?.windowScene?.statusBarManager?.statusBarFrame.height ?? 0
23
votes

Try, I have tried it.

let window = UIApplication.shared.windows.filter {$0.isKeyWindow}.first        
let height = window?.windowScene?.statusBarManager?.statusBarFrame.height ?? 0
13
votes

Solution:

This seems to work without any warning in iPhoneX+ devices as well.

Swift 4.2 / 5

if #available(iOS 13.0, *) {
    let window = UIApplication.shared.windows.filter {$0.isKeyWindow}.first
    statusBarHeight = window?.windowScene?.statusBarManager?.statusBarFrame.height ?? 0
} else {
    statusBarHeight = UIApplication.shared.statusBarFrame.height
}

Try it. Hope it Helps.

7
votes

I had the same problem as MMV (see their comment on Jordan H's answer), view.window?.windowScene?.statusBarManager?.statusBarFrame.height returned nil near the launch of my app. Specifically view.window returned nil. I then tried searching through the windows property on UIApplication.shared:

for window in UIApplication.shared.windows

This returned two windows in my case. Both of the windows had equal, non-nil status bar heights. I'm not sure why there were two windows, but at least both had the same status bar heights. I accessed the status bar heights like so:

if let height = window.windowScene?.statusBarManager?.statusBarFrame.height

I decided in my case that since there could possibly be different heights to take the largest of the heights for my code. Here is the code I used for my application:

let statusBarHeight: CGFloat = {
    var heightToReturn: CGFloat = 0.0
         for window in UIApplication.shared.windows {
             if let height = window.windowScene?.statusBarManager?.statusBarFrame.height, height > heightToReturn {
                 heightToReturn = height
             }
         }
    return heightToReturn
}()

Hope this helps someone!

6
votes
var statusBarHeight: CGFloat = 0
if #available(iOS 13.0, *) {
    statusBarHeight = UIApplication.shared.keyWindow?.windowScene?.statusBarManager?.statusBarFrame.height ?? 0
} else {
    statusBarHeight = UIApplication.shared.statusBarFrame.height
}
0
votes

for iOS 13:

in your SceneDelegate:

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
 if let windowScene = scene as? UIWindowScene {
    let window = UIWindow(windowScene: windowScene)
    let statusBarSize = windowScene.statusBarManager!.statusBarFrame
    ...// initialize your root view controller
 }
}

and if you want to pass the value to your Views, you can set the value as Environment and use it in your Views. example:

first we need to create our environment key:

struct StatusBarSizeEnvironmentKey: EnvironmentKey {
   public static let defaultValue: CGRect = CGRect()
}

extension EnvironmentValues {
  public var statusBarSize: CGRect {
    set { self[StatusBarSizeEnvironmentKey.self] = newValue }
    get { self[StatusBarSizeEnvironmentKey] }
  }
}

and set the value in SceneDelegate:

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
 if let windowScene = scene as? UIWindowScene {
    let window = UIWindow(windowScene: windowScene)
    let statusBarSize = windowScene.statusBarManager!.statusBarFrame
    window.rootViewController = UIHostingController(rootView: YourView()
             .environment(\.statusBarSize, statusBarSize))
 }
}
0
votes

Learning from the great answers already given here, using connectScenes from UIApplication, and we can make an extension:

extension UIApplication {
    var statusBarHeight: CGFloat {
        connectedScenes
            .compactMap {
                $0 as? UIWindowScene
            }
            .compactMap {
                $0.statusBarManager
            }
            .map {
                $0.statusBarFrame
            }
            .map(\.height)
            .max() ?? 0
    }
}

Usage:

let height = UIApplication.shared.statusBarHeight