Notification about an empty ListView in Xamarin.Forms

demo

ListView is one of my favourite UI controls available in Xamarin.Forms. It is mostly easy to use and customise. Just bind a collection of data, define the representation of each item and you are done!

However, there is one pitfall which most of the developers tend to ignore – if the bound collection is empty, the ListView will have nothing to show. Depending on the targeting platform it may look ugly or confusing for the end user.

In this blogpost we will check few possible solutions. One solution will be purely implemented on the ViewModel level and the other one will be a reusable ListView control wrapper.

Continue reading “Notification about an empty ListView in Xamarin.Forms”

Advertisements

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.

Repeater or Bindable StackLayout

Intro

When designing a View (Page) we need to take into consideration that there might be a lot of content to show. Typically we should use a ListView, which by default is scrollable. However, what if you have to show more than one ListView on a single page? Nesting ScrollViews is a very bad practice that should be avoided unless natively supported. In this case it will most probably make sense to put all the content within a single ScrollView. But how? Here is where the Repeater or BindableStackLayout comes into play.

Continue reading “Repeater or Bindable StackLayout”

Xamarin.Forms Recipe: Label with Letter Spacing

Setting a letter spacing for a Label in Android and iOS turned into an interesting research for me. I would expect that such a common task would be easily done with a help of a Renderer or an Effect. However, I was very surprised to discover that some platforms do not have a built-in support for setting letter spacing.

Continue reading “Xamarin.Forms Recipe: Label with Letter Spacing”

The ultimate guide to Emojis 🤹‍♂️

 

This slideshow requires JavaScript.

It is very common nowadays to express ourselves by using emojis. Instead of typing few words, we prefer to send one emoji that will express our feelings and emotions. Not sure that the other side will always get what we mean by sending a 🧞 , but that is another story.

In this article we will learn:

  1.  How to use emojis in static content like Labels and Buttons
  2.  How to define and use emoji in XAML only
  3.  How to define and use emoji using C#

Continue reading “The ultimate guide to Emojis 🤹‍♂️”

Most common mistakes beginners make in Xamarin.Forms

We live in a great time where technology evolves fast and we need to keep up with it if we want to stay relevant. Beside this, we also have to be productive, use the latest and greatest tools, implement the best available solutions and deliver on time. Following article mentions a list of most common mistakes we tend to do while using Xamarin.Forms.

Continue reading “Most common mistakes beginners make in Xamarin.Forms”

How to access localhost from Android emulator and iOS simulator?

Being lucky to develop a backend and a Xamarin.Forms clients on your own? Sooner or later you will have to debug the API calls and it might become painful. Unless, you will follow the next rules:

  1. Configure your API URL to run on 127.0.0.1 instead of a localhost:

    // .NET Core Web.Api example
    public static IWebHost BuildWebHost(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
    .UseStartup<Startup>()
    .UseUrls(“http://127.0.0.1:5001“)
    .Build();

  2. Configure your Xamarin.Forms API consumer to have a conditional URL base:

    string apiUrl = null;
    if (Device.RuntimePlatform == Device.Android)
    apiUrl = “http://10.0.2.2:5001/api“;
    else if (Device.RuntimePlatform == Device.iOS)
    apiUrl = “http://localhost:5001/api“;
    else
    throw new UnsupportedPlatformException();

The problem with Android emulator is that it maps 10.0.2.2 to 127.0.0.1, not to localhost. However, the iOS Simulator uses the host machine network.

That should be it!
Happy debugging!