Change .NET MAUI iOS Status Bar Color (Background)

At the moment I’m working on a little .NET MAUI Blazor app and out of the box the iOS target has a white status bar at the top. I’m no designer, but I don’t like how that looks. In this post I’ll show you how to add a nice status bar color, even when rotating the device!

Problem: No Status Bar Color/Transparent Status Bar

When you create a new .NET MAUI (Blazor) app there will be a white (or black when Dark Theme is enabled) bar on top of your screen. This really doesn’t have much to do with the fact that it’s .NET MAUI, this is just how iOS works. It’s not just white, it’s transparent even. If you scroll the Blazor content all the way to the top you can see the content appearing above the top bar of our Blazor content. You can see what that looks like below. Time to fix that!

Our not so good looking status bar

Change the iOS Status Bar Color… But with Xamarin.Forms

Actually we won’t actually change the status bar, we place a view behind the status bar. David Ortinau has a great 60 second video on how to do it for Xamarin.Forms, but! We are now on .NET MAUI, so let’s see how to do it there.

I first tried to port the code from David (see below) to .NET MAUI, but it seems something has changed in the lifecycle of things and I kept running into issues.

public class PageRenderer : Xamarin.Forms.Platform.iOS.PageRenderer
{
protected override void OnElementChanged(Xamarin.Forms.Platform.iOS.VisualElementChangedEventArgs e)
{
base.OnElementChanged(e);
if(e.OldElement != null || Element == null)
{
return;
}
try
{
UpdateStatusBarColor();
}
catch(Exception ex)
{
Debug.WriteLine($"Error: {ex.Message}");
}
}
private void UpdateStatusBarColor()
{
var bar = GetStatusBar();
bar.BackgroundColor = ColorConverters.FromHex("#fd7b38").ToPlatformColor();
}
private UIView GetStatusBar()
{
UIView statusBar;
if (UIDevice.CurrentDevice.CheckSystemVersion(13, 0))
{
int tag = 123; // Customize this tag as you want so we don't create it over and over
UIWindow window = UIApplication.SharedApplication.Windows.FirstOrDefault();
statusBar = window.ViewWithTag(tag);
if (statusBar == null)
{
statusBar = new UIView(UIApplication.SharedApplication.StatusBarFrame);
statusBar.Tag = tag;
statusBar.BackgroundColor = UIColor.Red; // Customize the view
window.AddSubview(statusBar);
}
}
else
{
statusBar = UIApplication.SharedApplication.ValueForKey(new NSString("statusBar")) as UIView;
}
return statusBar;
}
}
view raw PageRenderer.cs hosted with ❤ by GitHub

The issue I kept running into is that UIApplication.SharedApplication.Windows.FirstOrDefault(); would yield no result. On the one hand because that API seems to be deprecated since iOS 13 and on the other hand, or maybe because of that deprecation, that it would just give me null indicating that something might have changed in .NET MAUI lifecycles and when that window was created.

While trying to figure out how to get it to work, I found that .NET MAUI has great support for different lifecycle events now, and I could totally leverage that!

Change Status Bar Color with .NET MAUI!

With my newly acquired knowledge about the lifecycles in .NET MAUI I went ahead and opened the App.xaml.cs file and added an override for the CreateWindow event. At this point the window should be created, so now at least I knew that should not be a problem anymore, but there is still the deprecation of that iOS API.

With some search engine magic I found out that you’re now supposed to use the Windows property on the right scene. In .NET MAUI (or .NET for iOS for that matter) you can do that by using UIApplication.SharedApplication.Delegate.GetWindow();. Now to make sure it works great on both iOS 13+ as well as earlier iOS versions, I added some code for the old and the new APIs.

You can review the full code that I implemented below. Make sure to read the text below the code, because there are some extra things I want to point out.

public partial class App : Application
{
public App()
{
InitializeComponent();
MainPage = new MainPage();
}
#if __IOS__
private DisplayOrientation _lastOrientation;
protected override Window CreateWindow(IActivationState activationState)
{
Window window = base.CreateWindow(activationState);
window.Created += (s, e) =>
{
UpdateStatusBarColor();
};
window.Destroying += (s, e) =>
{
NSNotificationCenter.DefaultCenter.RemoveObserver(new NSString("UIDeviceOrientationDidChangeNotification"));
};
return window;
}
private void UpdateStatusBarColor()
{
UIView statusBar;
if (UIDevice.CurrentDevice.CheckSystemVersion(13, 0))
{
int tag = 4567890;
UIWindow window = UIApplication.SharedApplication.Delegate.GetWindow();
statusBar = window.ViewWithTag(tag);
if (statusBar == null || statusBar.Frame != UIApplication.SharedApplication.StatusBarFrame)
{
statusBar = statusBar ?? new(UIApplication.SharedApplication.StatusBarFrame);
statusBar.Frame = UIApplication.SharedApplication.StatusBarFrame;
statusBar.Tag = tag;
window.AddSubview(statusBar);
}
}
else
{
statusBar = UIApplication.SharedApplication.ValueForKey(new NSString("statusBar")) as UIView;
}
if (statusBar != null)
{
// TODO Make this color come from somewhere shared
statusBar.BackgroundColor = Color.FromArgb("#2B0B98").ToUIColor();
}
NSNotificationCenter.DefaultCenter.AddObserver(new NSString("UIDeviceOrientationDidChangeNotification"), NotificationCenter =>
{
// This gets called multiple times on iOS, let's optimize a little bit
if (_lastOrientation != DeviceDisplay.MainDisplayInfo.Orientation)
{
UpdateStatusBarColor();
_lastOrientation = DeviceDisplay.MainDisplayInfo.Orientation;
}
});
}
#endif
}
view raw App.xaml.cs hosted with ❤ by GitHub

In all this code you will notice that I’m tracking whenever the orientation of the device changes. For iOS, when you hold it in landscape on a phone, there will be no status bar visible at all. Without this code, if you would rotate, there would be this weird artefact on the screen. That is because we’re not really doing anything with the status bar directly, we’re just placing a view under it. Now to make this all work, whenever we don’t discover the status bar view (in other words: we’re running for the first time) or if we discover the device is rotated, we reevaluate if and how the status bar view should be recreated. Luckily, iOS gives us the UIApplication.SharedApplication.StatusBarFrame API that tells us where the status bar is and what the dimensions are.

Compiler Directives?!

Lastly, you can see how I wrapped all this in #if __IOS__ compiler directives. This is necessary because this is iOS specific code and of course, I only want to do this for iOS.

Personally, I hate them. But we’ll probably see much more of them since .NET MAUI has the single-project approach. There are definitely ways around them (see the linked video below), but for this case I decided to keep it like this.

How To Write Platform-Specific Code in .NET MAUI
Learn how to write platform-specific code without compiler directives in this video

The result of our code can be reviewed in the screenshot below.

Amazing looking status bar!

In Review

If you’re coming from Xamarin.Forms there are some different routes you need to discover to do things in .NET MAUI, but once you get used to that it feels much more natural.

I’m really happy with the new lifecycle management in .NET MAUI, let me know if you want to see more content on that.

More content on this blog coming and also make sure to subscribe to my YouTube channel!

1 thought on “Change .NET MAUI iOS Status Bar Color (Background)”

Comments are closed.