Adjusting font Size of Xamarin’s Picker (Part 2)

I already made 2 post about picker, but in this post I want to be more specific about picker in iOS especially about it’s text size. I already told how to do it in Android, now it’s iOS’s turn.  First thing first, now let’s review the custom picker class.

Custom Picker Class

This class actually is almost the same with class in my older post. The only thing I did is changing the name of FontSize property into TextSize. This is because I got warning saying that Font Size is hidden property that Picker already had. I didn’t get this warning when first time I created this class, so I made little adjustment here.

public class CustomPicker : Picker
{
    public static readonly BindableProperty TextSizeProperty =
        BindableProperty.Create(nameof(TextSize), typeof(float), typeof(CustomPicker), 24, BindingMode.TwoWay);

    public float TextSize
    {
        set { SetValue(TextSizeProperty, value); }
        get { return (Int32)GetValue(TextSizeProperty); }
    }

    public static BindableProperty PlaceholderColorProperty =
        BindableProperty.Create(nameof(PlaceholderColor), typeof(string), typeof(CustomPicker), DefaultColor, BindingMode.TwoWay);

    public string PlaceholderColor
    {
        get { return (string)GetValue(PlaceholderColorProperty); }
        set { SetValue(PlaceholderColorProperty, value); }
    }

    public static string DefaultColor => "#CCCCCC";
}

Picker iOS Renderer

Now let’s move on to the picker renderer in iOS project. This class also similar with a class with same name in my old post, when I discussed about placeholder color. The only difference is I added some lines of code to adjust it’s text size. And it’s actually a little bit tricky to do that.

To change the size of picker’s text in iOS, we need to create something called FontDescriptor. Ok, I’m not expert on swift programming, and maybe there’s better way to create FontDescriptor, but I did it by taking the descriptor of UILabel’s font. After I got the description, I made new Font descriptor from it with adjusted size property. Then I create new UIFont with that new description.

Lastly, I put my new font to NSAttributedString so I can update the AttributedPlaceholder and AttributedText property of the iOS picker. I did it so the placeholder and the text had same set of attributes.

public class CustomPickerRenderer: PickerRenderer
{
    protected override void OnElementChanged(ElementChangedEventArgs e)
    {
        base.OnElementChanged(e);
        if (e.NewElement != null)
        {
            var customPicker = e.NewElement as CustomPicker;

            // get Bindable properties
            UIColor placeholderColor = GetUIColor(customPicker.PlaceholderColor);
            float textSize = (float)customPicker.TextSize;

            // create font decsriptor
            var label = new UILabel();
            var fontDescriptor = label.Font.FontDescriptor;

            // adjusting font size
            var newDescriptor = fontDescriptor.CreateWithAttributes(new UIFontAttributes()
            {
                Size = textSize
            });
            UIFont font = UIFont.FromDescriptor(newDescriptor, 0);

            // set attributes
            var placeholderAttributes = new NSAttributedString(customPicker.Title, new UIStringAttributes()
            { ForegroundColor = placeholderColor, Font = font });

            Control.AttributedPlaceholder = placeholderAttributes;

            var textAttributes = new NSAttributedString(customPicker.Title, new UIStringAttributes()
            { ForegroundColor = placeholderColor, Font = font });

            Control.AttributedText = textAttributes;<span id="mce_SELREST_start" style="overflow:hidden;line-height:0;"></span>
        }
    }

    private UIColor GetUIColor(string color)
    {
        return UIColor.FromRGB(GetRed(color), GetGreen(color), GetBlue(color));
    }

    private float GetRed(string color)
    {
        Color c = Color.FromHex(color);
        return (float)c.R;
    }

    private float GetGreen(string color)
    {
        Color c = Color.FromHex(color);
        return (float)c.G;
    }

    private float GetBlue(string color)
    {
        Color c = Color.FromHex(color);
        return (float)c.B;
    }
}

 

Sample Code is available in my Github repo

Create your own Animated Bottom Picker

OK guys, I’m still playing around in animation in Xamarin, so in this post I want to try out two other animation functions, which are TranslateTo and FadeTo. In order to do that, I created my own picker that comes up from the screen’s bottom , just like picker you would get if you used Swift or React Native. The picker itself would not really looks similar like the picker in that platforms but it would kinda works like them. My goal in this post will be just exploring different kind of animation in Xamarin. In my last post, I already used RotateTo for accordion list view which was awesome and I’m exited to dig deeper about this stuff. So, let’s begin.

Absolute Layout

First thing first, we need to create a layout that enable us to put our “picker” outside the screen when the app started because the picker will be showing up from the bottom of the screen. So, I used Absolute Layout to put some elements off the screen. If you are not familiar with Absolute Layout, basically anything you put inside this layout can have 2 important properties. First, LayoutBonds which takes 4 parameters, X position, Y position, Width and Height. Second, LayoutFlags which is the setting of the LayoutBound, whether you want to use pixel or screen proportion for those 4 parameters. You can read the complete explanation in the documentation, in this post I will only discuss what I did with my absolute layout. So, let’s take a look at the code first.

BottomPickerCode

So, I put everything inside absolute layout, but I only used absolute layout’s properties in two elements. First, in a BoxView, which I used to the background of the picker. I set layout flag to All, means the layout bound will be the screen’s proportion. With the layout bound 0,0,1,1; I put the background in upper left corner (0,0) and I set it as big as the screen (1,1). I’ll do the same for the Frame, which will be the picker and contain a list view. I just set different proportion to make it bellow the screen and has half of the screen size. For user interaction, I only used familiar function like entry’s Focused, list view’s ItemSelected and also GestureRecognizer for the box view. Now, let’s move to code behind and add some animations into our screen.

The Animation

I created two methods, Show_Picker and Hide_Picker to execute the animation that will be accessed from our event handlers. Here’s the code.

public partial class PickerButtomPage : ContentPage
{
    public ObservableCollection CountryList;

    public PickerButtomPage()
    {
        InitializeComponent();
        CountryList = new ObservableCollection()
        {
            new Country("Indonesia"),
            new Country("India"),
            new Country("United States"),
            new Country("Brazil"),
            new Country("United Kingdom")
        };
        PickerContry.ItemsSource = CountryList;
    }

    void Handle_ItemTapped(object sender, ItemTappedEventArgs e)
    {
        ((ListView)sender).SelectedItem = null;
    }

    void Handle_ItemSelected(object sender, SelectedItemChangedEventArgs e)
    {
        if (e.SelectedItem == null)
            return;

        Country country = e.SelectedItem as Country;
        EntryCountry.Text = country.Name;

        Hide_Picker();
    }

    void Handle_Tapped(object sender, EventArgs e)
    {
        Hide_Picker();
    }

    void Handle_Focused(object sender, FocusEventArgs e)
    {
        Show_Picker();
    }

    void Show_Picker()
    {
        Background.FadeTo(0.4, 500, Easing.SinIn);
        PickerFrame.TranslateTo(0, -300, 500);
    }
void Hide_Picker()
    {
        Background.FadeTo(0, 500, Easing.SinOut);
        PickerFrame.TranslateTo(0, 300, 500);
        EntryHide.Focus();
    }
}

public class Country
{
    public Country(string name){ Name = name; }
    public string Name { get; set; }
}

So in Show_Picker, I faded in the background (box view) into opacity 0.4 in 0.5 second. Meanwhile I also moved the Frame Picker 300 pixels up, also in 0.5 second. I did the other way around for the Hide_Picker and then added one line to remove focus from the Entry.

Basically, this how my picker works. I trigger the picker to show up when the country entry is focused . Then when user choose one country or hide the picker by clicking the background, I set the focus to another entry I hide in the screen to prevent the user to type manually to the entry. And if it works as expected, it will look like this.

Set Placeholder Color of Xamarin’s Picker

In one of my previous post, we’ve discussed about how to adjust font size of Xamarin’s Picker. Font size in picker, especially in Android picker, is simply fixed, so we need a workaround if we want to adjust it. In iOS, font size of the picker is more flexible, you can set height request and width request, or simply put it on a grid, the size of whole picker will follow its container. But there’s another property of picker that also fixed. It’s the placeholder color. I know, most of the time we won’t need to change the placeholder color of the picker. However, for the sake of the app’s theme, sometime we gonna need to set color of the placeholder. So, In case you need it, you can use this method to set the placeholder color.

Custom Picker

To set the placeholder color, first thing we gonna need, of course, a bindable property for it. So we gonna update our CustomPicker from previous post and add Placeholder Color property in it

 

public class CustomPicker : Picker
{
    public static readonly BindableProperty FontSizeProperty =
        BindableProperty.Create(nameof(FontSize), typeof(Int32), typeof(CustomPicker), 24, BindingMode.TwoWay);

    public Int32 FontSize
    {
        set { SetValue(FontSizeProperty, value); }
        get { return (Int32)GetValue(FontSizeProperty); }
    }

    public static BindableProperty PlaceholderColorProperty =
        BindableProperty.Create(nameof(PlaceholderColor), typeof(string), typeof(CustomPicker), "#CCCCCC", BindingMode.TwoWay);

    public string PlaceholderColor
    {
        get { return (string)GetValue(PlaceholderColorProperty); }
        set { SetValue(PlaceholderColorProperty, value); }
    }
}

 

Custom Renderer Android

Next, we update the renderer in Android. In Android, representing the placeholder color, we have property called Hint Text Color. The only problem is Hint Text Color use Color class from Android.Graphic class unlike default Color of Xamarin from Xamarin.Form class. As a result, we need to parse the color. Fortunately, Android.Graphic.Color take hex code as its parameter so we can just simply parse it. The following code is how we do it.

public class CustomPickerRenderer : PickerRenderer
{
    protected override void OnElementChanged(ElementChangedEventArgs e)
    {
        base.OnElementChanged(e);
        this.Control.Background = Forms.Context.GetDrawable(Resource.Drawable.CustomPickerBackground);
        Control?.SetPadding(20, 20, 20, 20);
        if (e.OldElement != null || e.NewElement != null)
        {
            var customPicker = e.NewElement as CustomPicker;
            Control.TextSize *= (customPicker.FontSize * 0.01f);
            Control.SetHintTextColor(Android.Graphics.Color.ParseColor(customPicker.PlaceholderColor));
        }
    }
}

Custom Renderer iOS

Nah, it’s where things got tricky. To set placeholder color, or in iOS term, Foreground Color, we gonna need class named UIColor. And UIColor doesn’t take Hex Code as its parameter. It takes RGB color. Consequently, we need to parse Hex to RGB first. I made three little functions to take the color Red, Green and Blue from our Hex Code.

Finish with the color thing, next we gonna put that color into something, called NSAttributedString. So, what is NSAttributedString? First, Placeholder Attribute in iOS Picker is a NSAttributedString, so we need to make our own NSAttributedString if we want to modify our placeholder. Second, NSAttributedString has many attributes that you’re able to modify. In this example we just use the Foreground Color. but it has many more important attributes such as Background Color , Font, and Stroke Color.

So, this is how the custom renderer in iOS looks like.

public class CustomPickerRenderer: PickerRenderer
{
    protected override void OnElementChanged(ElementChangedEventArgs e)
    {
        base.OnElementChanged(e);
        if (e.OldElement != null || e.NewElement != null)
        {
            var customPicker = e.NewElement as CustomPicker;

            string placeholderColor = customPicker.PlaceholderColor;
            UIColor color = UIColor.FromRGB(GetRed(placeholderColor), GetGreen(placeholderColor), GetBlue(placeholderColor));

            var placeholderAttributes = new NSAttributedString(customPicker.Title,new UIStringAttributes()
            { ForegroundColor = color });

            Control.AttributedPlaceholder = placeholderAttributes;
        }
    }

    private float GetRed(string color)
    {
        Color c = Color.FromHex(color);
        return (float)c.R;
    }

    private float GetGreen(string color)
    {
        Color c = Color.FromHex(color);
        return (float)c.G;
    }

    private float GetBlue(string color)
    {
        Color c = Color.FromHex(color);
        return (float)c.B;
    }
}

Let’s try it out!

I made minor change to example I used in previous post. I add placeholder color and run it in Android and iOS emulator.


    
        
        
        
        
    

 

Sample Code is available in my Github repo.

Simple Way to bind Selected Item Changed to View Model

As we know together, in Xamarin we can bind command and command parameter to a button. That command will be fired when button is clicked and we can pass parameter through command parameter. But it’s kinda more complicated when it comes to picker or list view. We have selected index changed and item selected in picker and list view respectively to handle the event when user pick a certain item, but we need function in back-end to capture the event before we process it to view model. We also can use behavior to bind the event directly to view model, but it’s bit too complicated for me, especially to do simple task like this. And recently I found a simple workaround to address this problem.

Property Binding

What I do is binding the Selected Item property to certain variable in view model. The binding mode can be one way to source, or two way if you want to initialize the selected item. Continuing from my previous post, I will use picker instead of list view in this example. I will also use the custom picker that I used in previous post. But the trick will also works for list view.

I’ve created two pickers in xaml file and the value that bind to the second picker will be depended to what user choose in first picker.   Here’s how the code look like.

<?xml version="1.0" encoding="utf-8" ?>
 <ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:control="clr-namespace:App1.CustomControls" x:Class="App1.Pages.PizzaPage">
  <ContentPage.Content>
   <StackLayout VerticalOptions="Center" Padding="30,0,30,0">
    <Label Text="Choose your Pizza!!!"  TextColor="#118E8C" FontSize="Large"/>
    <control:CustomPicker Title="Select Pizza Country" ItemsSource="{Binding CountryList}" FontSize="48" SelectedItem="{Binding SelectedCountry, Mode=TwoWay}"/>
    <control:CustomPicker Title="Select Pizza Type" ItemsSource="{Binding PizzaList}" FontSize="42"/>
   </StackLayout>
  </ContentPage.Content>
 </ContentPage>

Selected Item Handling

As you can see, in the first picker, Selected Item property is bind to property named Selected Country. In the view model, when this variable is set, I will call function to update the list that is bind to the second picker. So, whenever user change his choice in first picker, the value of second picker will also change simultaneously. The following code is how the view model look like.

public class PizzaViewModel : INotifyPropertyChanged
{
  private ObservableCollection<string> countryList;
  private ObservableCollection<string> pizzaList;
  private string selectedCountry;

  public ObservableCollection<string> CountryList
  {
    get => countryList;
    set
    {
      SetObservableProperty(ref countryList, value);
    }
  }
  public ObservableCollection<string> PizzaList
  {
   get => pizzaList;
   set => SetObservableProperty(ref pizzaList, value);
  }

  public string SelectedCountry
  {
    get => selectedCountry;
    set
    {
      if (value != selectedCountry && value != null)
      LoadPizzaType(value);
      SetObservableProperty(ref selectedCountry, value);
    }
}

public event PropertyChangedEventHandler PropertyChanged;

public PizzaViewModel()
{
  LoadPizzaCountry();
}

private async void LoadPizzaCountry()
{
  CountryList = await PizzaServices.GetPizzaCountry();
}

private async void LoadPizzaType(string country)
{
  PizzaList = await PizzaServices.GetPizzaType(country);
}

protected void SetObservableProperty<T>(ref T field, T value,
[CallerMemberName] string propertyName = "")
{
  if (EqualityComparer<T>.Default.Equals(field, value)) return;
  field = value;
  OnPropertyChanged(propertyName);
}

protected virtual void OnPropertyChanged(string propertyName)
 => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

Take a look at variable Selected Country, it’s where the trick lies. With this simple trick, you can bind something similar like command and command parameter in one packet to picker and list view.

Adjusting font size of Xamarin’s Picker

When you’re developing the front-end side of a Xamarin’s app, you’re demanded to put and arrange every UI component in the right place, with right size and right color.  When it comes to size, it’s kinda troublesome to adjust picker to precise font size. The font size of picker in Xamarin, including DatePicker and TimePicker, is simply fixed. So, in this post, we will discuss about a workaround method to approach this problem. With this method, you will be even able to bind the font size of the picker to certain variable in view model if you want to. Note: so far, it works in Android, I still haven’t tried it in iOS. I’ll update this post or make another post after try it in iOS.

Custom Picker Class

The first step is we will create the custom class of the picker. This custom class will inherit picker class and we’ll add one binding property, font size, to this class. The following code is the example of the custom class.


 public class CustomPicker : Picker
    {
        public static readonly BindableProperty FontSizeProperty =
            BindableProperty.Create(nameof(FontSize), typeof(Int32), typeof(CustomPicker),
                10, BindingMode.Default);
        public Int32 FontSize
        {
            get => (Int32)GetValue(FontSizeProperty);
            set => SetValue(FontSizeProperty, value);
        }
    }

Updating Picker UI

The second step, we move on to the Android project. This part is just about the UI, nothing to do with the functionality of the picker. You can ignore and jump to next step if you feel don’t need it. So, in this part, I made and axml file which define the the background of the picker. As you know, standard Xamarin is just a text with underline, not so eye catching. So, I make little bit modification on it. I put the file in Drawable folder inside Resources.




  
    
      
      
      
    
  

  
    
      
      
      
    
  


Custom Picker Renderer

The third step is the most important part. We gonna make custom renderer of the picker. Inheriting from PickerRenderer class, we will adjust the text size property of the picker base on font size property that we’ve created earlier. I use 0.01 as the multiplier just because it suit my need in my previous project, you can change it to meet your need if you feel need to.


 class CustomPickerRenderer : PickerRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs e)
        {
            base.OnElementChanged(e);
            this.Control.Background = Forms.Context.GetDrawable(Resource.Drawable.customPicker);
            if (e.OldElement != null || e.NewElement != null)
            {
                var customPicker = e.NewElement as CustomPicker;
                Control?.SetPadding(20, 0, 0, 0);
                Control.TextSize *= (customPicker.FontSize * 0.01f);
            }

        }
    }

Implementation

Now let’s try it out. The following codes if the sample of xaml file where I use the custom picker, followed by the view model.




    
        
            
            
            
            
        
    


 public class PickerViewModel : INotifyPropertyChanged
    {

        private List countryList;
        private List stateList;
        private List cityList;

        public List CountryList
        {
            get => countryList;
            set => SetObservableProperty(ref countryList, value);
        }

        public List StateList
        {
            get => stateList;
            set => SetObservableProperty(ref stateList, value);
        }

        public List CityList
        {
            get => cityList;
            set => SetObservableProperty(ref cityList, value);
        }

        public event PropertyChangedEventHandler PropertyChanged;

        public PickerViewModel()
        {
            CountryList = new List() { "USA", "China", "Russia" };
            StateList = new List() { "California", "Sichuan", "Kirov" };
            CityList = new List() { "Los Angeles", "Guangzhou", "St. Petersburg" };
        }

        protected void SetObservableProperty(ref T field, T value,
            [CallerMemberName] string propertyName = "")
        {
            if (EqualityComparer.Default.Equals(field, value)) return;
            field = value;
            OnPropertyChanged(propertyName);
        }

        protected virtual void OnPropertyChanged(string propertyName)
            => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
[XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class PickerPage : ContentPage
    {
        public PickerPage()
        {
            InitializeComponent();
            BindingContext = new PickerViewModel();
        }
    }

When you run the project, you will have three different picker with three different size. You can try to bind the font size property with any integer variable you have within your view model.

 

Update : Set Placeholder Color of Xamarin’s Picker

Update : Adjusting Font Size of Xamarin’s Picker (Part 2)

Sample Code is Available in my Github repo