Eduardo Rosas' Blog

Learn Azure, Xamarin, WPF, and more!

Dynamic-Colored Progress Bars - iOS Custom Renderer in Xamarin Forms

Using XAML in Xamarin Forms we can change the accent color of the Progress Bars we define, and being able to set it to a HEX value, the possibilities are endless (well, not exactly, only about 16 million, but you know what I mean).

But what if you want that color to change dynamically, say, depending on how much the progress bar is filled, especially if you're going to keep your code-behind as clean as possible, so no handling of events and messy code.

analysis-business-businesswoman-955447.jpg

In this post, I will demonstrate how we can accomplish this by overriding the default behavior of the native iOS Progress Bar so that straight from the Progress Bar class itself we have that functionality implemented. To override this native functionality, we can use Custom Renderers, which is a way of defining in a completely native way, new feature or behaviors to our views.

Note: I published a video a few months ago in which I also explain how to customize a progress bar, though in that case I only changed the height and did nothing as exiting as in this one, feel free to check it out if you are interested:

Defining the Custom Renderer

Screen Shot 2019-01-09 at 18.28.15.png

I'll start by creating a new CustomRenderers folder inside the iOS project, and inside of this folder, I will define a new class that I will call CustomProgressBarRenderer. Nothing out of the ordinary just yet, but this class will have to be a ProgressBarRenderer, so we make it inherit from that class. Don't forget to add the necessary using directive to its namespace (Xamarin.Forms.Platform.iOS).

Exporting the Custom Renderer

Now we need a way to tell Xamarin Forms that this is the renderer to be used in place of a particular view (ProgressBar in our scenario). There are a couple of ways to achieve this, the easiest one, I think, is to define an assembly attribute to the namespace where these custom renderers are. If you have no idea what I'm talking about, fear not, it is a way to tell Xamarin Forms that a custom renderer will be available for use when a certain Xamarin Forms view is being rendered, and all that is required is a line of code that looks like this:

//! ADD THIS LINE
[assembly: ExportRenderer(typeof(ProgressBar), typeof(CustomProgressBarRenderer))]
namespace YourApp.iOS.CustomRenderers
{
    public class CustomProgressBarRenderer : ProgressBarRenderer
    {
      
    }
}

Note: Since this line is outside of the namespace that contains CustomProgressBarRenderer, you will need to add a using directive that points to it, even if this is the same file where it is defined.

Adding the custom functionality

With that line of code, our Custom Renderer is already being used by our application, if we were to define some ProgressBars (the type of the element to which the custom renderer is being exported), all the code inside the CustomProgressBarRenderer would be available. Of course, right now that is absolutely nothing, so let's fix that.

Since our new class is inheriting from the ProgressBarRenderer class, we now have access to native methods that define the Progress Bar on iOS, and so, we can start adding the functionality that we need.

Start by overriding the OnElementChanged method, which will be called everytime anything about the Progress Bar changes, including of course the Progress value, which we will use to set the color accordingly.

Right after the call to the base method, we will access the event arguments that the method receives (the e variable in my case, as you can notice below), locate the NewElement property, and evaluate its Progress property which, of course, contains exactly what we need to fulfill our purpose in this example.

Here, the evaluations are entirely up to you; you can define your own flow, your own values, and your own colors. But there are a couple of things that you need to keep in mind:

  • Always handle the scenario where the Progress is not a number, however unlikely, it is still best to be sure that all situations are covered.

  • Since we are accessing entirely native iOS libraries, the Control property (coming from the ProgressBarRenderer class) has access to a ProgressTintColor, which happens to be precisely what we need.

  • You can easily define any color with the FromHex method of the Color class, and then convert that to a UIColor (an iOS type) with the help of the mega-useful ToUIColor method.

Here is the code I came up with:

protected override void OnElementChanged(ElementChangedEventArgs<ProgressBar> e)
{
    base.OnElementChanged(e);

    if (double.IsNaN(e.NewElement.Progress))
        Control.ProgressTintColor = Color.FromHex("#00B9AE").ToUIColor();
    else if (e.NewElement.Progress < 0.3)
        Control.ProgressTintColor = Color.FromHex("#008DD5").ToUIColor();
    else if (e.NewElement.Progress < 0.5)
        Control.ProgressTintColor = Color.FromHex("#2D76BA").ToUIColor();
    else if (e.NewElement.Progress < 0.7)
        Control.ProgressTintColor = Color.FromHex("#5A5F9F").ToUIColor();
    else if (e.NewElement.Progress < 0.9)
        Control.ProgressTintColor = Color.FromHex("#B3316A").ToUIColor();
    else
        Control.ProgressTintColor = Color.FromHex("#e01a4f").ToUIColor();

    LayoutSubviews();
}
Simulator Screen Shot - iPhone XS Max - 2019-01-09 at 19.09.35.png

Now, there is some actual code that the ProgressBars that we define over on XAML can use. Here is a screenshot of an app that happens to be using this renderer, but you can quickly test how it works by defining a ProgressBar in ANY XAML Page and manually change the Progress value that you set.

What do you think? Are you going to be using this Custom Renderer? Or, now that you know about them, will you be coding your own?