Animation in Xamarin Forms with Airbnb Lottie

When it comes to animation, I think Lottie library from Airbnb is one of the most recommended free libraries available out there. It’s easy to use, light weight, and most importantly, can be used across platforms, including Xamarin Forms. So, in this post, I want to do a little introduction how to use this library, and I gonna using some animations that already available at LottieFiles.

Lottie Nuget Package

To start using this library, it’s actually pretty simple. Just go to NuGet Package Manager, look for NuGet called Com.Airbnb.Xamarin.Forms.Lottie and then install it. After you finish installing, you gonna see a readme text, looks like the picture below.

From that readme text, we already get example that cover some of most important events that can be handled by Lottie. It can handle a lot more, but for starter, I will discuss those three main events handler. I will walk you through it by giving simple example how to use it.

Animation Event Handlers

Base of its name, I think you’re already able to guess what function for each handler. PlaybackStartedCommand will be triggered when the animation start playing, in the other hand, PlaybackFinishedCommand triggered at animation finished. And if by any chance user click or tap on the AnimationView, ClickedCommand will handle it. 

I made a simple example to simulate that process. I created a program to show some animations. When the animation start animating, it gonna display the name of the animation file. When it finished, it gonna show how many times the animation already played. And then when user clicking, we gonna play another animation. You can see the sample code below.

public class LottieAnimationViewModel : BaseViewModel
{
    private string animationName;
    public string AnimationName
    {
        get => animationName;
        set => SetProperty(ref animationName, value);
    }

    private string animationPlayed;
    public string AnimationPlayed
    {
        get => animationPlayed;
        set => SetProperty(ref animationPlayed, value);
    }

    private int currentAnimation { get; set; }
    private int played { get; set; }
    private List<string> animationList { get; set; } 

    public ICommand PlayingCommand { get; set; }
    public ICommand FinishedCommand { get; set; }
    public ICommand ClickedCommand { get; set; }

    public LottieAnimationViewModel()
    {
        currentAnimation = 0;
        played = 0;
        animationList = new List<string>() 
        {
            "check_animation.json", "idea_bulb.json", "washing_machine.json"
        };
        AnimationName = animationList[currentAnimation];
        AnimationPlayed = SetAnimationPlayed(played);

        MessagingCenter.Send(this, "play");

        PlayingCommand = new Command(Playing);
        FinishedCommand = new Command(Finished);
        ClickedCommand = new Command(Clicked);
    }

    private void Playing()
    {
        AnimationName = animationList[currentAnimation];
    }

    private void Finished()
    {
        played++;
        AnimationPlayed = SetAnimationPlayed(played);
        MessagingCenter.Send(this, "play");
    }

    private void Clicked()
    {
        if (currentAnimation < 2)
            currentAnimation++;
        else
            currentAnimation = 0;

        played = 0;
        AnimationPlayed = SetAnimationPlayed(played);
        MessagingCenter.Send(this, "play");
    }

    private string SetAnimationPlayed(int number)
    {
        return $"Animation Played {number} times";
    }
}

You can see the complete source code of that example in my Github repo. If you run the code, it will look like the video below.

Handling Images

One little note before I end this post. When I tried this library in my project, one thing that little bit tricky is how to handle images. There’s a difference where to put it in Droid and iOS project. In Droid project, you put your JSON animation file in folder Asset. And if you have images, you have to create folder inside Asset to store the images. In Animation View you have to reference ImageAsset property to that folder. But in iOS project, unlike Android, you have to store both JSON file and images in Resources folder. It’s not gonna work if you create another folder inside resources to store your image . Your iPhone won’t show the images.

I think that’s all for this post. You can always refer to my Github for complete code and feel free to ask, Thank you.

Credit Animation

Sample Animations from LottieFiles

Washing Machine and idea bulb by Mohit Saini

Check animation by Joshua Oluwagbemiga 

Tracking down WebView URL’s Changes in Xamarin Forms

Hi guys, in this post I just want to share a simple tip that maybe you guys gonna need it. So, in my latest project, I created an app that had a webview on it. Among those web pages that I need to show, there was a certain page that I couldn’t display to the user because it didn’t work properly. In order to keep the business process run well, I had to navigate back to native page whenever user reach that page. In short, I need put a tracker on my webview. I look up to the internet but couldn’t find anything that suit my need so I came up with simple solution.

Custom WebView

The Original Xamarin Forms’s Webview of course didn’t support this tracking feature, even tough you bind a certain variable to source property of web view, it won’t change when you navigate to to other pages. So, we gonna need new property, I call it CurrentUrl.

public class CustomWebView : WebView
{
    public static BindableProperty CurrentUrlProperty =
        BindableProperty.Create(nameof(CurrentUrl), typeof(string), typeof(CustomWebView), null, BindingMode.TwoWay);

    public string CurrentUrl
    {
        get { return (string)GetValue(CurrentUrlProperty); }
        set { SetValue(CurrentUrlProperty, value); }
    }
}

I gonna use this new property to store the current url the web view is showing. No, let see how to store that current url.

WebView Delegate

Because my project requirement only for iOS, for now I just gonna show you how to do it in iOS. But I guess it won’t be much different in Android side.

So, I store the current URL on method ShouldStartLoad in UIWebViewDelegate. That method is run whenever you’re loading  a new page. This is also the method where you put header to web page, if it’s needed.  This is how the renderer and delegate look like.

public class CustomWebViewRenderer : WebViewRenderer
{
    protected override void OnElementChanged(VisualElementChangedEventArgs e)
    {
        base.OnElementChanged(e);
        Delegate = new CustomUiWebViewDelegate(this);
    }
}

public class CustomUiWebViewDelegate : UIWebViewDelegate
{
CustomWebViewRenderer customWebViewRenderer;
    public CustomUiWebViewDelegate(CustomWebViewRenderer _webViewRenderer = null)
    {
        customWebViewRenderer = _webViewRenderer ?? new CustomWebViewRenderer();
    }

    public override bool ShouldStartLoad(UIWebView webView, NSUrlRequest request, UIWebViewNavigationType navigationType)
    {
       var wv = customWebViewRenderer.Element as CustomWebView;
       wv.CurrentUrl = request.Url.ToString();
       return true;
    }
}

View Model

The last thing you need to do is binding that CurrentUrl property to certain variable in view model. So, whenever it changes, you can trigger something to be done. In the example below, I bind it to a variable with the same name, CurrentUrl.

public class WebViewViewModel : BaseViewModel
{
    private string currentUrl;
    public string CurrentUrl
    {
        get =&gt; currentUrl;
        set
        {
            SetProperty(ref currentUrl, value);
DoSomething(value);
        }
    }

    public WebViewViewModel()
    { }

    private void DoSomething(string url)
    {
        // actually do something here<span id="mce_SELREST_start" style="overflow:hidden;line-height:0;">&#65279;</span>
    }
}

Horizontal List View in Xamarin Forms

I know we have a nuget package for this stuff. I’ve never used it before, and I need this control in my current project, but then I found out that this nuget package is not compatible with the new .Net Standard 2.0 so I was not able to install it to my project. I was forced to look for another solution and thanks God, I found this awesome blog post about Carousel Layout by Chris Riesgo. That post is about Carousel, is not exactly about horizontal list view, but with little modification there and there, I got the horizontal list view I need. If you want to know more about Carousel Layout, you can go to that link, because I will just explain the adjustments I made.

Horizontal List View

This custom class is pretty similar with Chris Riesgo’s Carousel class. Basically, what you do in this class is putting a horizontal stack layout inside horizontal scroll view.  I then added two more bindable properties, which are ItemWithRequest and ItemHeightRequest , because unlike carousel, horizontal list view won’t take the whole width and height of the screen. I wanted a flexibility when  I set the size of list view’s item.

I also changed the user interaction method. This is actually the crucial part of my modification, because in carousel, whatever item show in screen is the SelectedItem, but horizontal list view shows more than one item on the screen. So, to handle ItemSelected  event, I remove the tapped event from the horizontal list view and then added TapGestureRecognizer to each list view’s item. This change enable me to detect which item have been tapped and update the value of SelectedItem to that item.

Below this is the final code of the custom Horizontal ListView class. And because we only use casual scrolling, we don’t need custom renderer in Android and iOS’s project.

public class HorizontalListView : ScrollView
{

    readonly StackLayout _stack;

    int _selectedIndex;

    TapGestureRecognizer tapGestureRecognizer;

    public HorizontalListView()
    {
        Orientation = ScrollOrientation.Horizontal;

        _stack = new StackLayout
        {
            Orientation = StackOrientation.Horizontal,
            Spacing = 0,

        };

        Content = _stack;

        tapGestureRecognizer = new TapGestureRecognizer();
        tapGestureRecognizer.Tapped += (s, e) => {
            var bindableObject = s as BindableObject;
            SelectedItem = bindableObject.BindingContext;
            UpdateSelectedIndex();
        };
    }

    public new IList Children
    {
        get => _stack.Children;
    }

    private bool _layingOutChildren;
    protected override void LayoutChildren(double x, double y, double width, double height)
    {
        base.LayoutChildren(x, y, ItemWidthRequest, ItemHeightRequest);
        if (_layingOutChildren) return;

        _layingOutChildren = true;
        foreach (var child in Children) child.WidthRequest = ItemWidthRequest;
        _layingOutChildren = false;
    }

    public static readonly BindableProperty SelectedIndexProperty =
        BindableProperty.Create(nameof(SelectedIndex), typeof(int), typeof(HorizontalListView), 0, BindingMode.TwoWay,
            propertyChanged: async (bindable, oldValue, newValue) =>
            {
                await ((HorizontalListView)bindable).UpdateSelectedItem();
            }
        );

    public int SelectedIndex
    {
        get
        {
            return (int)GetValue(SelectedIndexProperty);
        }
        set
        {
            SetValue(SelectedIndexProperty, value);
        }
    }

    async Task UpdateSelectedItem()
    {
        await Task.Delay(300);
        SelectedItem = SelectedIndex > -1 ? Children[SelectedIndex].BindingContext : null;
    }

    public static readonly BindableProperty ItemsSourceProperty =
        BindableProperty.Create( nameof(ItemsSource), typeof(IList), typeof(HorizontalListView), null,
            propertyChanging: (bindableObject, oldValue, newValue) =>
            {
                ((HorizontalListView)bindableObject).ItemsSourceChanging();
            },
            propertyChanged: (bindableObject, oldValue, newValue) =>
            {
                ((HorizontalListView)bindableObject).ItemsSourceChanged();
            }
        );

    public IList ItemsSource
    {
        get
        {
            return (IList)GetValue(ItemsSourceProperty);
        }
        set
        {
            SetValue(ItemsSourceProperty, value);
        }
    }

    void ItemsSourceChanging()
    {
        if (ItemsSource == null) return;
        _selectedIndex = ItemsSource.IndexOf(SelectedItem);
    }

    void ItemsSourceChanged()
    {
        _stack.Children.Clear();
        foreach (var item in ItemsSource)
        {
            var view = (View)ItemTemplate.CreateContent();
            var bindableObject = view as BindableObject;
            if (bindableObject != null)
                bindableObject.BindingContext = item;
            _stack.Children.Add(view);
            view.GestureRecognizers.Add(tapGestureRecognizer);
        }

        if (_selectedIndex >= 0) SelectedIndex = _selectedIndex;
    }

    public DataTemplate ItemTemplate
    {
        get;
        set;
    }

    public static readonly BindableProperty SelectedItemProperty =
        BindableProperty.Create(nameof(SelectedItem), typeof(object), typeof(HorizontalListView), null, BindingMode.TwoWay,
            propertyChanged: (bindable, oldValue, newValue) =>
            {
                ((HorizontalListView)bindable).UpdateSelectedIndex();
            }
        );

    public object SelectedItem
    {
        get
        {
            return GetValue(SelectedItemProperty);
        }
        set
        {
            SetValue(SelectedItemProperty, value);
        }
    }

    void UpdateSelectedIndex()
    {
        if (SelectedItem == BindingContext) return;

        SelectedIndex = Children
            .Select(c => c.BindingContext)
            .ToList()
            .IndexOf(SelectedItem);
    }

    public static readonly BindableProperty ItemWidthRequestProperty =
        BindableProperty.Create(nameof(ItemWidthRequest), typeof(Int32), typeof(HorizontalListView), 100, BindingMode.TwoWay);

    public Int32 ItemWidthRequest
    {
        set { SetValue(ItemWidthRequestProperty, value); }
        get { return (Int32)GetValue(ItemWidthRequestProperty); }
    }

    public static readonly BindableProperty ItemHeightRequestProperty =
        BindableProperty.Create(nameof(ItemHeightRequest), typeof(Int32), typeof(HorizontalListView), 100, BindingMode.TwoWay);

    public Int32 ItemHeightRequest
    {
        set { SetValue(ItemHeightRequestProperty, value); }
        get { return (Int32)GetValue(ItemHeightRequestProperty); }
    }

}

Model and ViewModel

To see what this horizontal list view looks like in action, I created a simple example. First, let’s go to the Model class. in this class, I created a model class with two properties on it.

public class HorizontalItem
{
    public string Title { get; set; }
    public string Icon { get; set; }
}

And then we move to the important part, the view model. I mentioned above that whenever user tap on an item, the SelectedItem property changes it’s value. So, what we need to do in view model is catch that triggered event. On this example, I only send message to the view class to show alert base on the item I click.

public class HorizontalViewModel : BaseViewModel
{
    private ObservableCollection horizontalItems;
    public ObservableCollection HorizontalItems
    {
        get => horizontalItems;
        set => SetProperty(ref horizontalItems, value);
    }

    private HorizontalItem selectedItem;
    public HorizontalItem SelectedItem
    {
        get => selectedItem;
        set
        {
            SetProperty(ref selectedItem, value);
            ItemSelected();
        }
    }

    public HorizontalViewModel()
    {
        HorizontalItems = new ObservableCollection()
        {
            new HorizontalItem() { Title = "One", Icon = "one.png" },
            new HorizontalItem() { Title = "Two", Icon = "two.png" },
            new HorizontalItem() { Title = "Three", Icon = "three.png" },
            new HorizontalItem() { Title = "Four", Icon = "four.png" },
            new HorizontalItem() { Title = "Five", Icon = "five.png" },
            new HorizontalItem() { Title = "Six", Icon = "six.png" },
            new HorizontalItem() { Title = "Seven", Icon = "seven.png" },
            new HorizontalItem() { Title = "Eight", Icon = "eight.png" },
            new HorizontalItem() { Title = "Nine", Icon = "nine.png" },
            new HorizontalItem() { Title = "Ten", Icon = "ten.png" },
        };
    }

    private void ItemSelected()
    {
        MessagingCenter.Send(this, "ItemSelected", SelectedItem);
    }
}

When you run the program, it will look like this.

Sample Code is available in my Github repo

 

Credit:

  • All Number Icon from FlatIcon by Roundicon
  • Xamarin.Forms Carousel View Receipe by Chris Riesgo (blog)

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); }
    }
}
<span id="mce_SELREST_start" style="overflow:hidden;line-height:0;">&#65279;</span>

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.

 

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.