3

Before iOS 13 I could change the status bar colour using the following bit of code:

        UIView statusBar = UIApplication.SharedApplication.ValueForKey(new NSString("statusBar")) as UIView;
        if (statusBar.RespondsToSelector(new ObjCRuntime.Selector("setBackgroundColor:")))
        {
            statusBar.BackgroundColor = UIColor.Clear.FromHex(0x323232);
            statusBar.TintColor = UIColor.White;
            app.StatusBarStyle = UIStatusBarStyle.BlackOpaque;
        }

However, on iOS13 I get the following runtime error

Objective-C exception thrown. Name: NSInternalInconsistencyException Reason: App called -statusBar or -statusBarWindow on UIApplication: this code must be changed as there's no longer a status bar or status bar window. Use the statusBarManager object on the window scene instead.

Any idea on how to change the status bar on iOS13?

EDIT: Just to point out, this is for Xamarin and not for Swift. To clarify the duplicate marker.

David Pilkington
  • 13,528
  • 3
  • 41
  • 73
  • 1
    Possible duplicate of [How to change the status bar background color and text color on iOS 13?](https://stackoverflow.com/questions/56651245/how-to-change-the-status-bar-background-color-and-text-color-on-ios-13) – Mac Sep 23 '19 at 20:06
  • @Mac How is this a duplicate? That is for swift, this is for Xamarin – David Pilkington Sep 23 '19 at 20:45
  • It's not duplicate .And here need a solution for xamarin ios . It seems like need a new way about setting background color of status bar . However , from apple document , there is no more detail about how to use [UIStatusBarManager](https://developer.apple.com/documentation/uikit/uistatusbarmanager?language=objc). – Junior Jiang Sep 24 '19 at 02:59
  • Error is the same, and the solution should be the same, you just have to convert the swift code to C# Xamarin code. That said, I did a quick test and KeyWindow is always nuil, so the answers from that dupe post fail. – jgoldberger - MSFT Sep 24 '19 at 03:00
  • @jgoldberger-MSFT saying that the solution is the same just because the error is the same seems a little bit of an over simplification given the 2 frameworks at play here. – David Pilkington Sep 24 '19 at 08:25
  • Not really, since one framework (Xamarin.iOS) is just a wrapper around the other framework (iOS SDK). Yes, some names are slightly changed to make them more C#/.NET friendly, but if you can do it in Obj-C / Swift, you can do the same in Xamarin.iOS. – jgoldberger - MSFT Sep 24 '19 at 19:34

3 Answers3

11

From error , you need to use UIStatusBarManager in IOS 13.

If you have updated VS to the latest version(Visual Studio 2019 version 16.3.0/Visual Studio 2019 for Mac version 8.3 above), you can change color as follow:

UIView statusBar = new UIView(UIApplication.SharedApplication.KeyWindow.WindowScene.StatusBarManager.StatusBarFrame);
statusBar.BackgroundColor = UIColor.Yellow;
UIApplication.SharedApplication.KeyWindow.AddSubview(statusBar);

Else the follow methods can make it works .

UIView statusBar = new UIView(UIApplication.SharedApplication.StatusBarFrame);
statusBar.BackgroundColor = UIColor.Yellow;
UIApplication.SharedApplication.KeyWindow.AddSubview(statusBar);

If the view is not fully rendered , UIApplication.SharedApplication.KeyWindow will return null .So you can change status bar color after fully rendered . Here is the sample .

===================================Update=================================

If in Forms project , you can have a try with invoking method in AppDelegate.cs

public override void OnActivated(UIApplication uiApplication)
{
    if (UIDevice.CurrentDevice.CheckSystemVersion(13, 0))
    {
        // If VS has updated to the latest version , you can use StatusBarManager , else use the first line code
        // UIView statusBar = new UIView(UIApplication.SharedApplication.StatusBarFrame);
        UIView statusBar = new UIView(UIApplication.SharedApplication.KeyWindow.WindowScene.StatusBarManager.StatusBarFrame);
        statusBar.BackgroundColor = UIColor.Red;
        UIApplication.SharedApplication.KeyWindow.AddSubview(statusBar);
    }
    else
    {
        UIView statusBar = UIApplication.SharedApplication.ValueForKey(new NSString("statusBar")) as UIView;
        if (statusBar.RespondsToSelector(new ObjCRuntime.Selector("setBackgroundColor:")))
        {
            statusBar.BackgroundColor = UIColor.Red;
            UIApplication.SharedApplication.StatusBarStyle = UIStatusBarStyle.BlackOpaque;
        }
    }
    base.OnActivated(uiApplication);
} 

Note :

Not sure this solurion will always work in AppDelegate.cs, better invoked in Controller.cs's method , because from iOS 13 , Apple have modified the architure of AppDelegate and added SceneDelegate to project.

Junior Jiang
  • 12,430
  • 1
  • 10
  • 30
  • Trying to access `UIApplication.SharedApplication.Windows[0]` throws an index out of range error – David Pilkington Sep 24 '19 at 08:53
  • @DavidPilkington Do you have a try with `KeyWindow` , it works in my project. And you can show where invoke this in project , in `AppDelegate / ViewDidLoad ` method will not work. That said , you can not directly use it in `FinishedLaunching` or `ViewDidLoad` , because View not rendered totally . You should invoke it in `ViewDidAppear` or other some event like Button click method , etc. – Junior Jiang Sep 24 '19 at 09:19
  • UIApplication.SharedApplication.StatusBarFrame will return CGRect. But in the solution, you are trying to convert into UIView. will it work? is it the working copy? – VasanthRavichandran Sep 24 '19 at 11:54
  • @VasanthRavichandran Yeah ,it will return `CGRect` . Your previous code also convert into `UIView` ,it will work . I will share my sample in answer . – Junior Jiang Sep 25 '19 at 01:10
  • @JuniorJiang-MSFT the sample link doesn't work. Where does that code above go? FinishedLaunching appears to be too early. I am looking for a place in AppDelegate to add that code. – John Livermore Nov 08 '19 at 15:39
  • @DavidPilkington did you find a solution? I would appreciate an update to your post if possible. Windows[0] is null for me as well, but not sure where to integrate the above code to fix this problem. – John Livermore Nov 08 '19 at 15:55
  • @JohnLivermore Hi , you can have a try in `ViewDidAppear` to invoke `Windows[0]` , it will not be null . – Junior Jiang Nov 11 '19 at 01:12
  • Is that in AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate? Bc I am not seeing an overload for ViewDidAppear for that class. And that's all I have on the iOS side of things. Are you saying this code goes in the Forms shared library? – John Livermore Nov 11 '19 at 16:39
  • 1
    @JohnLivermore If in `AppDelegate.cs` , there will not have `ViewDidAppear` . My shared answer is based on a Xamarin.iOS project . `ViewDidAppear` can be invoked in Controller . Now here u are a Forms project , you can invoke `OnActivated` Method in `AppDelegate.cs` . I will update answer . – Junior Jiang Nov 12 '19 at 01:58
0

This also works, if used in ViewDidAppear override in a UIViewController. I previously tested in ViewWillAppear and even that was too soon to have a KeyWindow non-null:

public override void ViewDidAppear(bool animated)
{
    base.ViewDidAppear(animated);
    if (UIDevice.CurrentDevice.CheckSystemVersion(13, 0))
    {
        //Obj-C: 
        // UIView *statusBar = [[UIView alloc]initWithFrame:[UIApplication sharedApplication].keyWindow.windowScene.statusBarManager.statusBarFrame] ;
        // statusBar.backgroundColor = [UIColor redColor];
        // [[UIApplication sharedApplication].keyWindow addSubview:statusBar];

        // Xamarin.iOS: 
        UIView statusBar = new UIView(UIApplication.SharedApplication.KeyWindow.WindowScene.StatusBarManager.StatusBarFrame);
        statusBar.BackgroundColor = UIColor.Red;
        UIApplication.SharedApplication.KeyWindow.AddSubview(statusBar);
    }
    else
    {
        UIView statusBar = UIApplication.SharedApplication.ValueForKey(new NSString("statusBar")) as UIView;
        if (statusBar.RespondsToSelector(new ObjCRuntime.Selector("setBackgroundColor:")))
        {
            statusBar.BackgroundColor = UIColor.Red;
            statusBar.TintColor = UIColor.White;
            UIApplication.SharedApplication.StatusBarStyle = UIStatusBarStyle.BlackOpaque;
        }
    }
}

And that is the same solution provided in the duplicate question: https://stackoverflow.com/a/58028658/2913599

jgoldberger - MSFT
  • 5,978
  • 2
  • 20
  • 44
-1

Could you please try the below solution. it works fine for me in the same scenario

if (@available(iOS 13.0, *)) {            
            UIView *statusBar = [[UIView alloc]initWithFrame:[UIApplication sharedApplication].keyWindow.windowScene.statusBarManager.statusBarFrame];
            statusBar.backgroundColor = [UIColor redColor];
            [[UIApplication sharedApplication].keyWindow addSubview:statusBar];

        } else {
            // Fallback on earlier versions
            UIView *statusBar=[[UIApplication sharedApplication] valueForKey:@"statusBar"];
            statusBar.backgroundColor = [UIColor redColor];
            [statusBar setNeedsDisplay];
        }