74

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?

Jordan H
  • 52,571
  • 37
  • 201
  • 351
  • 17
    @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 Oct 25 '19 at 13:57
  • 5
    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 Oct 25 '19 at 14:36

15 Answers15

96

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
shim
  • 9,289
  • 12
  • 69
  • 108
Jordan H
  • 52,571
  • 37
  • 201
  • 351
  • 17
    If you use it soon after the app launches it returns nil, while the old way would return the proper value. Any suggestions? – MMV Sep 22 '19 at 22:59
  • Do you all know if the status bar changes height now in IOS 13. I don't think so but I want to make sure. – Miguel Oct 19 '19 at 20:46
  • 2
    @jordan H, This is not working in iOS 13.3 in Xcode 11.3 Can you give update on this. – Mahesh NFC Jan 03 '20 at 06:49
  • use in viewWillLayoutSubviews(). – sergey_s Jun 24 '22 at 14:29
  • KeyWindow should be get as: static func getKeyWindow() -> UIWindow? { return UIApplication .shared .connectedScenes .flatMap { ($0 as? UIWindowScene)?.windows ?? [] } .first { $0.isKeyWindow } } – sabiland May 02 '23 at 08:28
34

Try, I have tried it.

let window = UIApplication.shared.windows.filter {$0.isKeyWindow}.first        
let height = window?.windowScene?.statusBarManager?.statusBarFrame.height ?? 0
陳韋中
  • 341
  • 3
  • 3
18

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.

Carmen
  • 6,177
  • 1
  • 35
  • 40
Ümañg ßürmån
  • 9,695
  • 4
  • 24
  • 41
17

UIApplication.shared.windows was deprecated in iOS 15.0

iOS 15

 let statusBarHeight = UIApplication.shared.connectedScenes
        .filter {$0.activationState == .foregroundActive }
        .map {$0 as? UIWindowScene }
        .compactMap { $0 }
        .first?.windows
        .filter({ $0.isKeyWindow }).first?
        .windowScene?.statusBarManager?.statusBarFrame.height ?? 0
NSSpeedForce
  • 127
  • 9
Den
  • 3,179
  • 29
  • 26
8
var statusBarHeight: CGFloat = 0
if #available(iOS 13.0, *) {
    statusBarHeight = UIApplication.shared.keyWindow?.windowScene?.statusBarManager?.statusBarFrame.height ?? 0
} else {
    statusBarHeight = UIApplication.shared.statusBarFrame.height
}
Taazar
  • 1,545
  • 18
  • 27
Andrew Bell
  • 89
  • 1
  • 1
7

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!

Ed Manning
  • 93
  • 1
  • 5
4

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
vicegax
  • 4,709
  • 28
  • 37
4

For the latest method of getting statusBarHeight, is as follows:

    private lazy var statusBarHeight: CGFloat = {
        var statusBarHeight: CGFloat = 0
        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
        }
        return statusBarHeight
    }()
Johnny
  • 1,112
  • 1
  • 13
  • 21
3

Worked for me for iOS 15 and higher.

extension UIViewController {
    var statusBarHeight: CGFloat {
        guard
            let scene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
            let height = scene.statusBarManager?.statusBarFrame.height
        else {
            return 0
        }
        return height
    }
}
2

For Objective-C

I'm not sure what is alternative of filter in OC, so I used a for loop to get keyWindow. Well, the actual height I get in iPhone 12 is '47' and iPhone 8 with '20'.

    CGFloat statusBarHeight;
    if (@available(iOS 13, *)) {
        NSArray *windows = UIApplication.sharedApplication.windows;
        UIWindow *keyWindow = nil;
        
        for (UIWindow *window in windows) {
            if (window.isKeyWindow) {
                keyWindow = window;
                break;
            }
        }
        statusBarHeight = keyWindow.windowScene.statusBarManager.statusBarFrame.size.height;
        NSLog(@"statusBarHeight: %f", statusBarHeight);
    } else {
        statusBarHeight = UIApplication.sharedApplication.statusBarFrame.size.height;
    }
Zhou Haibo
  • 1,681
  • 1
  • 12
  • 32
  • +1 for showing how to get and use keyWindow with windowScene. My old app has no windowScene. All I want is the statusBar size. Also, you posted just four hours before I needed this! – Jeff Sep 12 '21 at 09:32
  • Glad to help, I'm a Swift dude, recently start to get some Objective-C knowledge. Nowadays, Objective-C stuff is quite rare in the internet, though most of big companies requires Objective-C first. – Zhou Haibo Sep 12 '21 at 10:04
1

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))
 }
}
Mohammad Rahchamani
  • 5,002
  • 1
  • 26
  • 36
0

stolen from accepted answer:

guard let sbheight = view.window?.windowScene?.statusBarManager?.statusBarFrame.height else {
     assertionFailure("usage error: VC lifecyclewise it becomes available no earlier than viewDidAppear")
}
Anton Tropashko
  • 5,486
  • 5
  • 41
  • 66
0

For Objective-C

- (CGFloat)statusBarHeight{
    UIWindowScene * scene = nil;
    for (UIWindowScene* wScene in [UIApplication sharedApplication].connectedScenes){
        if (wScene.activationState == UISceneActivationStateForegroundActive){
            scene = wScene;
            break;
        }
    }
    return scene.statusBarManager.statusBarFrame.size.height;
}

I think the above is OK too, @ChuckZHB's answer.

dengST30
  • 3,643
  • 24
  • 25
0

This only works if you have a single window app, which is all of the mobile apps and 99% of the iPad apps.

let height = UIApplication.shared.windows.first(where: \.isKeyWindow)?.windowScene?.statusBarManager?.statusBarFrame.height ?? 0

or if you like to break up the code a bit:

let window = UIApplication.shared.windows.first(where: \.isKeyWindow)
let height = window?.windowScene?.statusBarManager?.statusBarFrame.height ?? 0
budiDino
  • 13,044
  • 8
  • 95
  • 91
0

Get statusBarHeight in iOS 15

In Objective C

UIWindowScene *windowScene = (UIWindowScene *)[UIApplication sharedApplication].connectedScenes.allObjects.firstObject;
UIWindow *keyWindow = nil;
if ([windowScene isKindOfClass:[UIWindowScene class]]) {
    NSArray<UIWindow *> *windows = windowScene.windows;
    for (UIWindow *window in windows) {
        if (window.isKeyWindow) {
            keyWindow = window;
            break;
        }
    }
}

CGRect statusFrame = keyWindow.windowScene.statusBarManager.statusBarFrame;
CGFloat *statusBarHeight = statusFrame.size.height;

In Swift

var keyWindow: UIWindow?
if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
   let sceneDelegate = windowScene.delegate as? SceneDelegate {
    let windows = windowScene.windows
    keyWindow = windows.first(where: \.isKeyWindow)
}

let statusFrame = keyWindow.windowScene.statusBarManager.statusBarFrame
let statusBarHeight = statusFrame.size.height