As an iOS Developer, creating immersive user experiences is one of the most important and sometimes challenging tasks. Whether you are developing a game, a video playback app, or a full-screen onboarding flow, there is one system interface element that often gets in the way of total immersion: the status bar.
In the modern era of Swift programming, SwiftUI has revolutionized the way we build interfaces, moving away from complex UIKit delegates and controllers toward a declarative approach. However, this paradigm shift also means that simple tasks, like hiding the status bar in SwiftUI, are done differently and constantly evolve with each new version of Xcode.
In this comprehensive tutorial, we will deeply explore how to handle the visibility of the status bar using Swift, covering not only iOS but also the specificities of macOS and watchOS. If you want to master SwiftUI and take your iOS Developer skills to the next level, join us on this journey.
1. Understanding the Status Bar in the Apple Ecosystem
Before diving into the source code in Xcode, it is essential to understand exactly what we are trying to hide, as the concept varies depending on the device.
- iOS/iPadOS: The status bar is the area at the top of the screen that displays the time, network signal, battery level, and on newer devices, it integrates with the notch or the Dynamic Island.
- macOS: On Macs, there isn’t a “status bar” per se inside the application window. Instead, we have the Menu Bar (system-wide) and the Title Bar (window-specific). For the purpose of this tutorial, we will address how to create immersive windows by hiding the title bar.
- watchOS: On the Apple Watch, space is vital. The status bar generally only shows the time in the top right corner.
Controlling these elements requires a deep understanding of Swift programming and how SwiftUI interacts with the different underlying operating systems.
2. How to Hide the Status Bar in SwiftUI for iOS
In iOS, the way to hide the status bar has changed in recent iterations of SwiftUI. Apple has been refining the API to make it more consistent with how other bars (like the navigation bar or tab bar) are handled.
The Modern Approach (iOS 15 and newer)
Starting with iOS 15, Apple introduced a much more robust and semantically correct modifier: .toolbar(...). As a good iOS Developer, you should always aim to use the latest APIs when your app’s version support allows it.
To hide the status bar in SwiftUI using the modern approach, you must use .toolbar(.hidden, for: .statusBar).
import SwiftUI
struct ImmersiveScreenView: View {
@State private var hideBar = true
var body: some View {
ZStack {
// Immersive background
Color.black.ignoresSafeArea()
VStack(spacing: 20) {
Image(systemName: "play.circle.fill")
.resizable()
.scaledToFit()
.frame(width: 100, height: 100)
.foregroundColor(.white)
Text("Full Screen Video Player")
.font(.title)
.fontWeight(.bold)
.foregroundColor(.white)
.multilineTextAlignment(.center)
Button(action: {
withAnimation {
hideBar.toggle()
}
}) {
Text(hideBar ? "Show Status Bar" : "Hide Status Bar")
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(10)
}
}
}
// This is the key modifier in iOS 15+
.toolbar(hideBar ? .hidden : .visible, for: .statusBar)
}
}
struct ImmersiveScreenView_Previews: PreviewProvider {
static var previews: some View {
ImmersiveScreenView()
}
}
Key takeaways from the code:
- Interactivity: We have bound the visibility to a
@Statevariable. This is extremely useful if you have media players where tapping the screen makes the controls appear or disappear along with the status bar. - Animation: Wrapping the state change in
withAnimationmakes the status bar slide smoothly up or down, giving a premium feel to your SwiftUI application.
The Legacy Approach (iOS 13 and iOS 14)
If your project requirements in Xcode force you to support iOS 13 or 14, you will need to use the older modifier. Although it is deprecated in newer versions, it is necessary to know it.
import SwiftUI
struct LegacyView: View {
@State private var hideBar = true
var body: some View {
Text("Support for iOS 14 and older")
.statusBar(hidden: hideBar) // Old modifier
}
}
Hiding the Status Bar Globally (Info.plist)
If your application is, for example, a game developed in Swift and you need to hide the status bar in SwiftUI throughout the entire application from the moment it launches (even during the loading or Launch Screen), doing it per view is not enough.
You will need to modify your project’s configuration file in Xcode:
- Go to the navigation panel in Xcode and select the root of your project.
- Go to the Info tab of your Target.
- Add a new key called
Status bar is initially hidden(UIStatusBarHidden) and set its value toYES. - Add another key called
View controller-based status bar appearance(UIViewControllerBasedStatusBarAppearance) and set it toNO.
Note for the iOS Developer: If you set the view controller-based appearance to
NO, you will lose the ability to dynamically change the color or visibility of the status bar in individual views using SwiftUI modifiers. Use this method only if your app is always 100% full screen.
3. Handling the Safe Area Alongside the Status Bar
A common mistake in Swift programming when hiding the status bar is forgetting the Safe Area. The status bar lives within the top margin of the safe area.
If you simply hide the bar, your content will still respect that invisible margin (leaving a blank space at the top), unless you explicitly tell SwiftUI to ignore it.
To achieve true full screen, you must combine hiding the bar with .ignoresSafeArea().
import SwiftUI
struct FullScreenImageView: View {
var body: some View {
Image("landscape_hd")
.resizable()
.scaledToFill()
// 1. Ignore safe edges (Notch, Dynamic Island, Home Indicator)
.ignoresSafeArea()
// 2. Hide the status bar
.toolbar(.hidden, for: .statusBar)
}
}
4. Hiding the “Status Bar” (Title Bar) in macOS
When we bring our SwiftUI experience to the Mac thanks to technologies like Mac Catalyst or by creating a native macOS app in Xcode, the paradigm changes.
In macOS, users expect to see the Menu Bar at the top of the screen. What we usually want to hide to create immersive experiences (like a styled settings window or a freeform design app) is the window’s own Title Bar.
In SwiftUI for macOS, this is controlled directly in the application’s lifecycle (App), not in the individual view.
import SwiftUI
@main
struct MyMacApp: App {
var body: some Scene {
WindowGroup {
ContentView()
// Prevent content from overlapping with close/minimize buttons
.edgesIgnoringSafeArea(.top)
}
// Hide the default title bar and its background
.windowStyle(.hiddenTitleBar)
.windowToolbarStyle(.unifiedCompact)
}
}
By using .windowStyle(.hiddenTitleBar), the standard gray bar disappears, allowing your view’s content to go all the way to the top edge of the window. The traffic light buttons (close, minimize, maximize) will continue to float over your content, giving you the responsibility as a developer to provide enough padding so they don’t interfere with your user interface.
5. Hiding the Status Bar in watchOS
The Apple Watch is a device where every pixel counts. By default, watchOS always displays the time in the upper right corner of your application.
In most productivity or health apps, keeping the time visible is a strict rule from Apple’s Human Interface Guidelines. However, if you are building an experience where the time is distracting (for example, a deep meditation screen or a simple wrist game), you can request the system to hide it.
Unlike iOS, watchOS has its own specific modifier in SwiftUI: .statusBarHidden().
import SwiftUI
struct WatchRelaxationView: View {
@State private var breathing = false
var body: some View {
VStack {
Circle()
.fill(Color.teal)
.scaleEffect(breathing ? 1.5 : 0.8)
.animation(.easeInOut(duration: 4).repeatForever(autoreverses: true), value: breathing)
.onAppear {
breathing = true
}
Text("Inhale...")
.font(.headline)
.padding(.top)
}
// Hides the time on the Apple Watch
.statusBarHidden(true)
}
}
Design Warning: Apple is very strict during the App Store review process for watchOS. You should only hide the status bar in SwiftUI on the watch if it is absolutely critical to the functionality of your current view. If the user loses track of time because of your app without a good justification, you could face a rejection.
6. Integrating SwiftUI with UIKit: The Hybrid Case
Although SwiftUI is the future (and the present) of Swift programming, the daily reality for an iOS Developer is that many codebases are still deeply rooted in UIKit.
If you have a UIViewController hosting a SwiftUI view using a UIHostingController, you’ll notice that sometimes SwiftUI modifiers like .toolbar(.hidden, for: .statusBar) don’t work as expected. This is because UIKit is still the “boss” in the view hierarchy, and its preferences override SwiftUI’s.
To resolve this in hybrid Xcode projects, you must create a subclass of UIHostingController and override the prefersStatusBarHidden property.
import SwiftUI
import UIKit
// 1. Create a subclass of UIHostingController
class ImmersiveHostingController<Content: View>: UIHostingController<Content> {
// 2. Override the status bar preference
override var prefersStatusBarHidden: Bool {
return true // Return true to always hide it in this controller
}
// Optional: You can also control the style (light or dark)
override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}
}
// Usage in your UIKit code:
// let swiftUIView = ImmersiveScreenView()
// let hostingController = ImmersiveHostingController(rootView: swiftUIView)
// self.present(hostingController, animated: true)
This technique ensures that the communication between UIKit and SwiftUI is clear and that the operating system respects your intention to hide the bar.
7. Best Practices and Common Mistakes (Developer Checklist)
As an iOS Developer, writing code that compiles in Xcode is only half the job. The other half is ensuring a flawless user experience. Here is a checklist when working with status bar visibility:
- Avoid UI Jumps: If you hide the status bar suddenly, all your view’s content below it might jump upwards (as the top space is suddenly freed up). To avoid this, always wrap dynamic state changes in a
withAnimationblock. - Beware of the Notch and Dynamic Island: Hiding the status bar does not make the front-facing camera hardware or the Dynamic Island of modern iPhone Pros disappear. If you are making a full-screen app (with
.ignoresSafeArea()), ensure no interactive buttons (like an “X” to close) are hidden behind the Notch. Use the Safe Area layout guides to position your buttons, even when ignoring the Safe Area for your background. - Navigation Consistency: If you are inside a
NavigationStack(or the oldNavigationView), hiding the status bar while keeping the navigation bar visible usually looks like a graphical glitch. If you are going to hide the status bar, it is a good practice to also hide the navigation bar using.toolbar(.hidden, for: .navigationBar). - Use the Environment: If the logic to show or hide the bar depends on multiple layers of views in your app, consider creating a variable in the
@Environmentor using a State Object pattern so you don’t have to pass bindings through dozens of views.
8. Conclusion
Mastering how to hide the status bar in SwiftUI is an essential skill in your Swift programming arsenal. What used to require complex manipulations at the view controller level in UIKit has now been distilled into elegant declarative modifiers across Apple’s different operating systems.
Whether you are building the next great immersive iOS app, fine-tuning a clean window in macOS, or creating a focused experience in watchOS, SwiftUI and Xcode provide you with the necessary tools to achieve pixel-perfect control over your interface.
Remember: with great power comes great responsibility. Only hide the status bar when it significantly improves the user experience. Transparency, access to battery life, and the clock are vital for mobile users, so give them control whenever possible.