statusBarManager?.statusBarFrame.height in UIKit, or use safeAreaInsets.top when laying out views. In SwiftUI, wrap your content in a GeometryReader and read geometry.safeAreaInsets.top instead of hardcoding values or calling the deprecated UIApplication.shared.statusBarFrame.Why this happens
Hardcoding the iOS status bar height—such as assuming 20 points on older devices or 44–59 points on devices with a notch or Dynamic Island—breaks across hardware generations, orientations, and multitasking states. On iPhone and iPad, the status bar height changes depending on the device’s top hardware cutouts, whether the status bar is hidden, or whether an active system banner (such as an ongoing call, navigation, or audio recording) is present.
In older versions of UIKit, developers queried UIApplication.shared.statusBarFrame. That API had two major caveats: it did not always swap width and height reliably during landscape rotations, and Apple deprecated it in iOS 13 when multi-window support (UIWindowScene) was introduced. In a scene-based app, each window scene manages its own status bar state.
Before reaching for the exact status bar height, it is also worth distinguishing between the status bar frame and the top safe area inset. For almost all UI layout tasks, safeAreaInsets.top is what you actually want because it accounts for the status bar as well as any hardware sensor housing.
How to fix it
1. Use UIWindowScene.statusBarManager in UIKit (iOS 13+)
The supported way to read the exact status bar height in UIKit is through statusBarManager on the UIWindowScene that hosts your view.
Inside a UIViewController, wait until the view is attached to a window (for example, in viewDidLayoutSubviews() or viewDidAppear(_:)) so that view.window?.windowScene is non-nil:
import UIKit
final class ExampleViewController: UIViewController {
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let statusBarHeight = view.window?.windowScene?.statusBarManager?.statusBarFrame.height ?? 0
print("Status bar height: \(statusBarHeight)")
}
}
If you need the status bar height outside of a view controller—such as in a helper or coordinator—look up the foreground active UIWindowScene from UIApplication.shared.connectedScenes:
import UIKit
func currentStatusBarHeight() -> CGFloat {
let windowScene = UIApplication.shared.connectedScenes
.compactMap { $0 as? UIWindowScene }
.first { $0.activationState == .foregroundActive }
return windowScene?.statusBarManager?.statusBarFrame.height ?? 0
}
Note that statusBarManager?.statusBarFrame returns CGRect.zero (height 0) when the status bar is hidden.
2. Use safeAreaInsets.top for UIKit layout
If your goal is to position a custom header, banner, or floating button below the top system chrome rather than measuring the status bar itself, read safeAreaInsets.top instead of statusBarFrame. Auto Layout constraints pinned to view.safeAreaLayoutGuide.topAnchor handle this automatically without manual math.
When you need the numeric value in code:
import UIKit
final class HeaderViewController: UIViewController {
override func viewSafeAreaInsetsDidChange() {
super.viewSafeAreaInsetsDidChange()
let topInset = view.safeAreaInsets.top
print("Top safe area inset: \(topInset)")
}
}
viewSafeAreaInsetsDidChange() is called whenever the safe area changes—such as during device rotation or when a status bar visibility animation occurs—making it safer than reading the value once in viewDidLoad().
3. Read safeAreaInsets.top in SwiftUI with GeometryReader
SwiftUI views respect safe areas by default, so you rarely need the raw status bar height. When you do need the top inset (for example, to extend a custom background under the status bar while keeping text aligned below it), use GeometryReader:
import SwiftUI
struct StatusBarAwareView: View {
var body: some View {
GeometryReader { geometry in
VStack(spacing: 12) {
Text("Top safe area: \(Int(geometry.safeAreaInsets.top)) pt")
.font(.headline)
Spacer()
}
.frame(maxWidth: .infinity)
.padding(.top, geometry.safeAreaInsets.top)
}
.ignoresSafeArea(edges: .top)
}
}
Because .ignoresSafeArea(edges: .top) is applied to the GeometryReader, geometry.safeAreaInsets.top reports the full top safe area height rather than 0. Without .ignoresSafeArea(edges: .top), the GeometryReader is already inset by the safe area, so geometry.safeAreaInsets.top inside it would read 0.
4. Legacy UIApplication.shared.statusBarFrame (pre-iOS 13 only)
If you are maintaining an older codebase that still targets iOS 12 or earlier, you may see UIApplication.shared.statusBarFrame. Because older iOS versions did not always orient statusBarFrame to match landscape mode, the standard workaround was taking the smaller of width and height:
import UIKit
// Deprecated in iOS 13+; use UIWindowScene.statusBarManager instead
func legacyStatusBarHeight() -> CGFloat {
let size = UIApplication.shared.statusBarFrame.size
return min(size.width, size.height)
}
Do not use this in new code: Xcode flags statusBarFrame as deprecated in iOS 13 and later, and it is not multi-window safe on iPadOS.
How to prevent it
- Prefer
safeAreaLayoutGuideand SwiftUI’s automatic safe area handling over manual height calculations whenever you are positioning views. - Never hardcode pixel or point constants like
20,44, or59for the status bar; iPhone and iPad models vary significantly. - Avoid reading
windoworsafeAreaInsetsinviewDidLoad(), because the view is not yet in the window hierarchy and both values will benilor0. - React to updates in
viewDidLayoutSubviews()orviewSafeAreaInsetsDidChange()so your layout stays accurate after rotation or status bar transitions.
FAQ
Why is statusBarManager?.statusBarFrame.height returning 0?
This usually happens for one of two reasons: either you are reading view.window?.windowScene too early (such as in viewDidLoad() before the view is added to a window), or the status bar is currently hidden by the view controller or app configuration. Move the read to viewDidLayoutSubviews() or viewDidAppear(_:).
Should I use statusBarFrame.height or safeAreaInsets.top?
Use safeAreaInsets.top for view layout and positioning, because it accounts for the status bar as well as hardware cutouts like the notch or Dynamic Island. Use statusBarManager?.statusBarFrame.height only when you specifically need the height of the system status bar itself.
Does statusBarManager?.statusBarFrame work in SwiftUI?
Yes, you can call the UIWindowScene lookup helper from SwiftUI if you specifically need the status bar frame rather than the safe area. However, for almost all SwiftUI layouts, reading geometry.safeAreaInsets.top inside a GeometryReader (with .ignoresSafeArea(edges: .top)) is cleaner and updates automatically with SwiftUI’s layout pass.
Parts of this guide are adapted from content licensed under CC BY-SA 3.0; this article is shared under the same license.
