Customizing Xamarin Editor

Another post about editor. In my previous post. I’ve shared about how you can add a placeholder to Editor using Behavior. In this post we’ll go little bit further. We still have an editor with placeholder, but when user start typing on it, the placeholder will turn in to it’s title. So it will make our editor little bit more interactive to user. Let’s start with creating our custom editor.

Custom Editor

I don’t make any update about this class. This class is still exactly same like the same class in my older post. It has 2 additional properties, Placeholder and Placeholder Color. But Placeholder Color will have more rules in this editor, because we will use same color for placeholder and title. Here’s the unchanged code of the Custom Editor class.

public class CustomEditor : Editor
{
    public static BindableProperty PlaceholderProperty =
        BindableProperty.Create(nameof(Placeholder), typeof(string), typeof(CustomEditor), string.Empty, BindingMode.TwoWay);

    public string Placeholder
    {
        get { return (string)GetValue(PlaceholderProperty); }
        set { SetValue(PlaceholderProperty, value); }
    }

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

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

Custom Content View

Now let’s handle the user interaction. We won’t be using behavior anymore in this post, because you can’t generate title just by using it. Instead, we’ll be making a custom content view. This is gonna look similar like when I created Entry with Title, but it will be little bit trickier, because unlike entry, editor doesn’t have placeholder by default. Let’s take a look at the xaml file.

TitledEditorCode

As you can see, we create similar xaml file like the entry. We have a label above custom editor to become the title. We also move all user interaction from behavior into this file. We handle The BindingContextChangedFocused, Unfocused and TextChanged event. With those four event handler, we’ll manage when the placeholder or the title is displayed. Let’s see the code behind it.

public partial class TitledEditor : ContentView
{
    public TitledEditor()
    {
        InitializeComponent();

        EditorContent.BindingContext = this;
        LabelTitle.BindingContext = this;
    }

    public static BindableProperty PlaceholderProperty =
        BindableProperty.Create(nameof(Placeholder), typeof(string), typeof(TitledEditor), string.Empty, BindingMode.TwoWay);

    public string Placeholder
    {
        get { return (string)GetValue(PlaceholderProperty); }
        set { SetValue(PlaceholderProperty, value); }
    }

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

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

    public static BindableProperty TextProperty =
        BindableProperty.Create(nameof(Text), typeof(string), typeof(TitledEditor), string.Empty, BindingMode.TwoWay);

    public string Text
    {
        get { return (string)GetValue(TextProperty); }
        set { SetValue(TextProperty, value); }
    }

    public static BindableProperty FontSizeProperty =
        BindableProperty.Create(nameof(FontSize), typeof(int), typeof(TitledEditor), 16, BindingMode.TwoWay);

    public int FontSize
    {
        get => (int)GetValue(FontSizeProperty);
        set => SetValue(FontSizeProperty, value);
    }

    public static BindableProperty TextColorProperty =
        BindableProperty.Create(nameof(TextColor), typeof(Color), typeof(TitledEditor), Color.Default, BindingMode.TwoWay);

    public Color TextColor
    {
        get { return (Color)GetValue(TextColorProperty); }
        set { SetValue(TextColorProperty, value); }
    }

    void Handle_ContainerFocused(object sender, FocusEventArgs e)
    {
        EditorContent.Focus();
    }

    void Handle_BindingContextChanged(object sender, EventArgs e)
    {
        if (string.IsNullOrEmpty(Text))
        {
            Text = Placeholder;
            LabelTitle.IsVisible = false;
            TextColor = Color.FromHex(PlaceholderColor);
        }
        else
        {
            LabelTitle.IsVisible = true;
            TextColor = Color.Default;
        }
    }

    void Handle_Focused(object sender, FocusEventArgs e)
    {
        LabelTitle.IsVisible = true;

        if (Placeholder == Text)
        {
            Text = string.Empty;
        }
    }

    void Handle_Unfocused(object sender, FocusEventArgs e)
    {
        if (string.IsNullOrEmpty(Text))
        {
            Text = Placeholder;
            LabelTitle.IsVisible = false;
            TextColor = Color.FromHex(PlaceholderColor);
        }
    }

    void Handle_TextChanged(object sender, TextChangedEventArgs e)
    {
        if (string.IsNullOrEmpty(Text))
        {
            LabelTitle.IsVisible = true;
            TextColor = Color.FromHex(PlaceholderColor);
        }
        else
        {
            if (Text != Placeholder)
            {
                LabelTitle.IsVisible = true;
                EditorContent.TextColor = Color.Default;
            }
            else
            {
                EditorContent.TextColor = Color.FromHex(PlaceholderColor);
            }
        }
    }
}

Let me walk you through it. First, when the binding context changed, it means when in initialized, it will check whether the editor have text on it or not. If it doesn’t have one, the placeholder will become the text with the color of placeholder color. We won’t have any title on it. But if it has text on it, the text color will be default and we will show the title with color of placeholder color. Basically, we do the similar checking to the other three events. The placeholder and title just keep switching place depend on the text of the editor and both of them have same color.

Titled Editor in action

 

It’s all it takes to make Editor with placeholder and title. And if you try to use the editor in one of you page, it will look like this.

Sample Code is available in my Github repo

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

Github Announcement

Some of you guys have asked me to put sample code on Git. So, today is the day, I’ve created a Github account and and put some of the sample code in a repository there. You can access my repo in this link. Until I created this post, I already added 2 sample code, but I will add more code soon in the future. Here’s the sneak peak of the sample code.