Maui icon indicating copy to clipboard operation
Maui copied to clipboard

[Proposal] StatusBarEffect

Open TheCodeTraveler opened this issue 4 years ago • 4 comments

StatusBarEffect

  • [x] Proposed
  • [ ] Prototype: Not Started
  • [ ] Implementation: Not Started
    • [ ] iOS Support
    • [ ] Android Support
    • [ ] macOS Support
    • [ ] Windows Support
  • [ ] Unit Tests: Not Started
  • [ ] Sample: Not Started
  • [ ] Documentation: Not Started

Summary

Set the color and style of the status bar

Detailed Design

StatusBarEffect.shared.cs

public class StatusBarEffect : RoutingEffect
{
  public static readonly BindableProperty ColorProperty;
  public static readonly BindableProperty StyleProperty;
  
  public static Color GetColor(BindableObject bindable);
  public static StatusBarStyle GetStyle(BindableObject bindable);
}

Usage Syntax

XAML Usage

<ContentPage local:StatusBarEffect.Color="Blue">

</ContentPage>

C# Usage

class MyPage : ContentPage
{
  public MyPage()
  {
     StatusBarEffect.SetColor(this, Colors.Black);
  }
}

TheCodeTraveler avatar Sep 26 '21 19:09 TheCodeTraveler

We should hold this until this https://github.com/dotnet/maui/pull/2049 get merged or closed

pictos avatar Oct 05 '21 02:10 pictos

Would like to implement this, in case issue is unblocked

maxkoshevoi avatar Oct 05 '21 07:10 maxkoshevoi

dotnet/maui#2049 is now closed, can we move forward with thins issue?

maxkoshevoi avatar Jan 30 '22 10:01 maxkoshevoi

This is still blocked by #124

pictos avatar Feb 06 '22 17:02 pictos

any updates here?

KSemenenko avatar Aug 15 '22 15:08 KSemenenko

@KSemenenko not yet. I'll circle back on .NET MAUI team to see if this will be in the framework or not

pictos avatar Aug 15 '22 15:08 pictos

Hi @pictos, what's the state on this one?

I'm a bit confused with the different issues here and in the MAUI project... Are you going to implement it in the CommunityToolkit or will the MAUI team add it directly in MAUI? And has any work on it already started?

I'll need the feature for a customer project... So if nothing has been implemented yet (nor is in the pipeline to do so shortly), any tips on how to implement it myself? I need to set it dynamically (in case the user switches themes on runtime), currently just for iOS and Android.

Thanks in advance :) Any info/help is appreciated!

EPS-Lac avatar Sep 07 '22 13:09 EPS-Lac

Hey @EPS-Lac, it's WIP I should have a PR by the end of this week. I hope this can be in the next stable release, if don't I can guide you on how to implement the feature or how to use the nuget generated by the PR.

pictos avatar Sep 07 '22 14:09 pictos

Let's make sure we wait for this one for the next release. A lot of people are waiting for this one. Unless something really weird happens, then we'll make a release first while we work on this.

jfversluis avatar Sep 07 '22 14:09 jfversluis

Would like to implement this, in case issue is unblocked

@pictos Oh, I thought, I would be assigned to port this one..

maxkoshevoi avatar Sep 07 '22 14:09 maxkoshevoi

Probably since it has been a while we forgot to check if you were still available. On to the next one! 😉

jfversluis avatar Sep 07 '22 15:09 jfversluis

good news :) waiting for it :)

KSemenenko avatar Sep 07 '22 15:09 KSemenenko

@jfversluis How about https://github.com/CommunityToolkit/Maui/issues/103? =)

maxkoshevoi avatar Sep 07 '22 15:09 maxkoshevoi

The PR linked to this issue will not implement the StatusBarStyle, and it's due to an API change on Apple.

The current way to do this is overriding the preferredStatusBarStyle and we can't do that here... The change should happen in .NET MAUI itself providing a way to control it.

More info here.

IF anyone knows an implementation that works, let me know

pictos avatar Sep 09 '22 02:09 pictos


  public enum StatusBarStyle
  {
    Default,
    LightContent,
    DarkContent,
  }

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"));
    };
    MAUICallback.OnBackgroundColorChanged += MAUICallbackOnOnBackgroundColorChanged;
    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);
        }
        
        // this is my variable with style
        var uiStyle = MAUICallback.StatusBarStyle switch
        {
            StatusBarStyle.LightContent => UIStatusBarStyle.LightContent,
            StatusBarStyle.DarkContent => UIStatusBarStyle.DarkContent,
            _ => UIStatusBarStyle.Default
        };

        UIApplication.SharedApplication.SetStatusBarStyle(uiStyle, false);
    }
    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(MAUICallback.CurrentColor).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;
        }
    });
}

than call "UpdateStatusBarColor();" when you need to change color. this code from App.xaml.cs

KSemenenko avatar Sep 09 '22 07:09 KSemenenko

@KSemenenko, first of the all thanks for the code snippet. The StatusBarStyle get updated? And what place do you override your CreateWindow method?

pictos avatar Sep 09 '22 14:09 pictos

this is in App.xaml.cs, I also will sent you all my file:

using System.Globalization;
using Winkt.Mobile.Api.Abstractions;
using Winkt.Mobile.Helpers;
using Winkt.Mobile.Managers;
using Winkt.Mobile.Managers.Interfaces;
using Xamarin.CommunityToolkit.Effects;
#if __IOS__
using Foundation;
using Microsoft.Maui.Controls.Compatibility.Platform.iOS;
using UIKit;
#endif

namespace Winkt.Mobile;

public partial class App : Application
{
    public App()
    {
        InitializeComponent();
        MainPage = new MainPage();
    }

    private void MAUICallbackOnOnBackgroundColorChanged(object sender, string e)
    {
#if __IOS__
        UpdateStatusBarColor();
#endif
    }

    protected override void OnStart()
    {
        WinktStaticHelper.GenerateDeviceID();

        var apiClient = WinktServiceProvider.GetService<IApiClientService>();
        apiClient.AuthApiClient.SetAuthToken(AppSettings.SessionId);
    }

    protected override void OnResume()
    {
        var manager = WinktServiceProvider.GetService<IAuthManager>();
        _ = manager.ResumeSession();
    }

    protected override void OnSleep()
    {
        var manager = WinktServiceProvider.GetService<IAuthManager>();
        _ = manager.PauseSession();
    }

#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"));
        };
        MAUICallback.OnBackgroundColorChanged += MAUICallbackOnOnBackgroundColorChanged;
        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);
            }
            

            var uiStyle = MAUICallback.StatusBarStyle switch
            {
                StatusBarStyle.LightContent => UIStatusBarStyle.LightContent,
                StatusBarStyle.DarkContent => UIStatusBarStyle.DarkContent,
                _ => UIStatusBarStyle.Default
            };
            UIApplication.SharedApplication.SetStatusBarStyle(uiStyle, false);
        }
        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(MAUICallback.CurrentColor).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
}

In mine implementation, I have some static class, and event, so it's not best solution for sure.

KSemenenko avatar Sep 09 '22 15:09 KSemenenko

as you can see, the problem is that this thing works on current page. as in Xamarin.Forms the problem is the same. To completely solve this issue, we need to put these 2 properties in Page class. and then when page changes its IsVisible property (or becomes visible) then we will need to applay these properties to paint the status bar.

KSemenenko avatar Sep 09 '22 15:09 KSemenenko

well, changing the status bar color works like a charm, with the current implementation that I've for the PR... The StyleBar doesn't, so I can't change the DarkContent to LightContent... From my research is because apple just allows the dynamic in ViewController, when you override the preferredStatusBarStyle method. But we can't do that here, this need to be done on .NET MAUI side. I'll try this window implementation and see how that works and see if I can abstract that here. Thanks a lot for all this input @KSemenenko

pictos avatar Sep 09 '22 16:09 pictos

Maybe it's time to implement our Page? base on Maui Page? and put all logic there?

like CommunityPage ?

because, if Xamarin.Froms I relay need this IsVisible readonly property, to know which page in the screen right now

KSemenenko avatar Sep 09 '22 17:09 KSemenenko

@KSemenenko I tested your code on my end... And the status bar content keeps in white color. So this doesn't work as well or did something wrong :/ Do you have a project that you can share ?

pictos avatar Sep 09 '22 21:09 pictos

this is my original PR https://github.com/xamarin/Xamarin.Forms/pull/8298

I will create demo app today, because for me this issue is big blocker, and I will help you as much as need to have this functional :)

KSemenenko avatar Sep 10 '22 08:09 KSemenenko

@pictos did you add into info.plist

<key>UIViewControllerBasedStatusBarAppearance</key>
 <false/>

KSemenenko avatar Sep 10 '22 11:09 KSemenenko

@pictos it will take more time, it my first time for custom rendered for MAUI, maybe you have some code, so I can working with it?

KSemenenko avatar Sep 10 '22 14:09 KSemenenko

Maui-StatusBarStule.zip done iOS works

KSemenenko avatar Sep 10 '22 14:09 KSemenenko

ios_andoird.zip iOS + and/or I love MAUI :)

so for iOS this is the issue

<key>UIViewControllerBasedStatusBarAppearance</key>
 <false/>

KSemenenko avatar Sep 10 '22 15:09 KSemenenko

Hey @KSemenenko I could confirm it works at runtime. Thanks for the snippet

pictos avatar Sep 10 '22 16:09 pictos

@pictos in my pr you can also find uwp code. Anyway maybe you need some help?

KSemenenko avatar Sep 10 '22 17:09 KSemenenko

@KSemenenko yeah, we are porting this feature from XCT, which is based on your Forms PR. The UWP doesn't work because on Maui we use the WinUI3 APIs, and they didn't implement the ability to change the status bar color manually.

pictos avatar Sep 10 '22 17:09 pictos

@pictos realy? Omg….. it least we will have it for iOS and Android :)

if you need some help with anything just ping me

KSemenenko avatar Sep 10 '22 17:09 KSemenenko