Dynamically changing the status bar appearance in Xamarin.Forms

 

Aug-18-2018 13-11-19

Usually there is a need in changing the status bar appearance to match the application theme at least once. In more advanced cases the appearance of the status bar may change multiple times, due different colour themes on different screens within the application.

Status bar appearance is about it’s background and text colours. Both properties has their own limitations on different platforms, however we could manipulate both with the solution described below.

Our goal is simple, we want to be able to switch the status bar appearance between  LightTheme and DarkTheme at runtime:

public interface IStatusBarStyleManager
{
    void SetLightTheme();
    void SetDarkTheme();
}

Android

Background colour

Since Android Lollipop (21) it is possible to set a custom status bar background colour by simply defining it in style.xml with a key  colorPrimaryDark or programatically (check below).

Text colour

Since Android M (23) it is possible to set a predefined status bar text colour theme to light or dark.

Implementation

public class StatusBarStyleManager : IStatusBarStyleManager
{
    public void SetDarkTheme()
    {
        if (Build.VERSION.SdkInt >= BuildVersionCodes.M)
        {
            Device.BeginInvokeOnMainThread(() =>
            {
                var currentWindow = GetCurrentWindow();
                currentWindow.DecorView.SystemUiVisibility = 0;
                currentWindow.SetStatusBarColor(Android.Graphics.Color.DarkCyan);
            });
        }
    }

    public void SetLightTheme()
    {
        if (Build.VERSION.SdkInt >= BuildVersionCodes.M)
        {
            Device.BeginInvokeOnMainThread(() =>
            {
                var currentWindow = GetCurrentWindow();
                currentWindow.DecorView.SystemUiVisibility = (StatusBarVisibility)SystemUiFlags.LightStatusBar;
                currentWindow.SetStatusBarColor(Android.Graphics.Color.LightGreen);
            });
        }
    }

    Window GetCurrentWindow()
    {
        var window = CrossCurrentActivity.Current.Activity.Window;

        // clear FLAG_TRANSLUCENT_STATUS flag:
        window.ClearFlags(WindowManagerFlags.TranslucentStatus);

        // add FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS flag to the window
        window.AddFlags(WindowManagerFlags.DrawsSystemBarBackgrounds);

        return window;
    }
}

Please note that I am using Current Activity Plugin for Xamarin.Android in order to get a reference to the current displayed activity.

iOS

Background colour

In iOS the status bar background colour by default matching the colour of the navigation bar. In other words, we don’t have to explicitly set the background colour of the status bar if we want it to match the background colour of the navigation bar.

Text colour

Since iOS 7 it is possible to set a predefined status bar text colour theme to light or dark. However, we will have to manipulate the Info.plist. Since status bar behaviour is determined by view controllers by default, we have to disable this:

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

Next, we can define a default text colour theme:

<key>UIStatusBarStyle</key>
<string>UIStatusBarStyleDefault</string>

Implementation

public class StatusBarStyleManager : IStatusBarStyleManager
{
    public void SetDarkTheme()
    {
        Device.BeginInvokeOnMainThread(() =>
        {
            UIApplication.SharedApplication.SetStatusBarStyle(UIStatusBarStyle.LightContent, false);
            GetCurrentViewController().SetNeedsStatusBarAppearanceUpdate();
        });
    }

    public void SetLightTheme()
    {
        Device.BeginInvokeOnMainThread(() =>
        {
            UIApplication.SharedApplication.SetStatusBarStyle(UIStatusBarStyle.Default, false);
            GetCurrentViewController().SetNeedsStatusBarAppearanceUpdate();
        });
    }

    UIViewController GetCurrentViewController()
    {
        var window = UIApplication.SharedApplication.KeyWindow;
        var vc = window.RootViewController;
        while (vc.PresentedViewController != null)
            vc = vc.PresentedViewController;
        return vc;
    }
}

Conclusion

At first this topic may seem very confusing, especially on Android, however it turned out to be very simple and easily achievable as you can se above. The code can be found on github.

Advertisements

Retrieving Facebook User Access Token in Xamarin.Forms

Few months ago I wrote an article about Using Native Facebook Login Button in Xamarin.Forms where I explained how to retrieve user access token using Facebook SDK. It is still a valid read and a good solution, however, recently I discovered that there is a shorter way to achieve almost the same thing.

In this article we will learn how to retrieve Facebook user access token, using a custom button (not native) with just a few lines of code. I assume that you know how to create a Facebook application and configure your Android and iOS projects. If I am mistaken please refer to my previous article.

There is an open source project Facebook Client Plugin that allow you to login, share and query Facebook using static API. It has a good documentation and good well known developers behind it. As you may already understand we are going to use it to retrieve user access token from Facebook in our project.

Here is the recipe:

  1. Create and configure Facebook app (more info here or here).
  2. Add Plugin.FacebookClient NuGet package to .NET Standard and platform targeting projects.
  3. Configure platform targeting projects (more info here or here).
  4. Add a custom Facebook login button to your Page/View.
  5. Bind a command to the previously created button with the next code:
    var fbLoginResponse=
        awaitCrossFacebookClient.Current.LoginAsync(newstring[]{“email”});
    if(fbLoginResponse.Status == FacebookActionStatus.Completed)
    {
        var fbUserAccessToken=CrossFacebookClient.Current.ActiveToken;
        // TODO: Use the fb access token
    }
    else
    {
        // Something went wrong
    }

That should be it!

You could create a wrapper interface and class to make this code testable, to be able to inject it as a dependency and to adjust it to your needs. This solution might be less configurable, however, the integration is very easy and the amount of code is miserable compare to the previous solution.

Big thanks to CrossGeeks!

Visual Studio for Mac tips & tricks

Switching from old good VS (for Windows) to a new cool VS for Mac can be painful. Original VS was released in 1997 (according to wikipedia) while VS for Mac was released only in 2016. Yes, it is based on XamarinStudio which is built on MonoDevelop but it still has a long way to go in order to be close to it’s ancient relative.

In this article we will take a look on VS for Mac “hidden gems” that can optimize and smooth your workflow. All you have to do is to open Mac’s VS Preferences and read this article on the side.

Environment

Font

One of the very basic yet very important settings is the Font. While there is nothing bad using the default font, FiraCode could beautify your code and improve it’s readability by replacing sequences of characters by a single ligature.

The project repository on github contains detailed information on how to install it in your system. It’s also great because this font can be used almost everywhere.

Continue reading “Visual Studio for Mac tips & tricks”

Using Native Facebook Login Button in Xamarin.Forms

This slideshow requires JavaScript.

Very often I hear questions like “Is there any Facebook SDK for Xamarin.Forms? Because there is one for Android Xamarin Native.”. This kind of questions can apply to different SDK that are compatible only with specific platforms and cannot be consumed directly in Xamarin.Forms. The answer to these questions usually is “If there is an available native Xamarin SDK it can be consumed in your Xamarin.Forms project. All you have to do is to create an abstraction like you would do with any other platform specific code.”.

As you might already understand, in this article we will create an abstraction over Xamarin.Facebook.Android and Xamarin.Facebook.iOS in order to display a native Facebook Login Button and handle the authentication related events in our Xamarin.Forms application.

Continue reading “Using Native Facebook Login Button in Xamarin.Forms”

Android emulators stopped working on macOS 10.13 (High Sierra)

Recently I updated my macOS to High Sierra however this broke my android emulators. Non of them started and logs showed something like:

Failed to open vm 3
Failed to create HAX VM
No accelerator found.
failed to initialize HAX: Invalid argument

As the logs states there is a problem with HAXM, and to be more specific a time for update has come. To solve the issue simply download the latest and greatest version of HAXM and restart your mac.

Installing Xamarin components

Xamarin components are easy to install, all you have to do is to download a zip, extract the content and to reference the dlls in your project. This would work if you download the component manually from components gallery. Same thing can be done using Visual Studio in a more robust way via ‘Components’ under the targeting platform projects. Either way, the component will be downloaded and installed only for a specific project.

Sometimes it might be useful to keep the component in cache, so it will be available globally. This is where the XAM file extension comes into play. Generally speaking, it is just an archive, you can change the file extension to zip and use it in a regular way, or vice versa change zip to xam.

Regardless of your OS you have to download xamarin-component.exe.
On Windows execute the next command:

xamain.component.exe install <component.xam>

On MacOS execute the next command with the same executable:

mono xamain.component.exe install <component.xam>

If the command executed successfully, you should see the installed component:

Screen Shot 2017-11-14 at 21.20.30

Please note the “Included in this project” and “Installed on this machine”. Components under the second section will be always there unless you will clean the cache. On MacOS the components will be installed under ‘~/Library/Caches/Xamarin’. Please let me know where the cached components are stored on Windows.

INotifyPropertyChange without boilerplate code in Xamarin.Forms

Implementing INotifyPropertyChange is pretty straightforward. Usually, you create a base ViewModel class which implements it and which usually contains RaisePropertyChanged method:

public abstract class BaseViewModel : INotifyPropertyChanged
{
    #region INotifyPropertyChanged
    public event PropertyChangedEventHandler PropertyChanged;

    protected void RaisePropertyChanged(
        [CallerMemberNamestring propertyName = )
    {
        PropertyChanged?.Invoke(thisnew PropertyChangedEventArgs(propertyName));
    }
    #endregion
}

Now you can extend the BaseViewModel and use it this way:

public class UserViewModel : BaseViewModel
{
    private string login;
    public string Login
    {
        get
        {
            return login;
        }
        set
        {
            if (login == value)
                return;
            login = value;
            RaisePropertyChanged();
        }
    }
    private string password;
    public string Password
    {
        get
        {
            return password;
        }
        set
        {
            if (password == value)
                return;
            password = value;
            RaisePropertyChanged();
        }
    }
}

For very small applications it can be a good enough approach, however, in bigger applications it turns into a lot of boring boilerplate code. Here is where NotifyPropertyChanged.Fody comes into play! With this nice package our code will turn into:


[ImplementPropertyChanged]
public abstract class BaseViewModel {}

public class UserViewModel : BaseViewModel
{
    public string Login { getset; }
    public string Password { getset; }
}

Easy as it is! I highly recommend to get familiar with the documentation as it contains a lot of useful information about more advanced flows. For example, if you need to RaisePropertyChange for dependent properties or to skip equality comparison.