Dropdown Control In Xamarin.iOS

Introduction

In this article, we will learn how to create Dropdown in Xamarin.Forms (Xamarin.iOS). As we learned in the previous article about by default, the Android Platform has dropdown called “Spinner”, but the iOS Platform has no dropdown like Android Spinner and how to achieve the dropdown in Xamarin.Forms for Android Platform using Custom renderer approach. In this article, we are going to create a custom renderer to achieve the dropdown for the iOS platform in Xamarin.Forms.

Dropdown in Xamarin.Forms

Please refer to the previous article to know, how to achieve the dropdown in the Android Platform. I have done changes commonly for Android and iOS in this article.

We will go for custom renderer approach to have a new control to wrap a Platform specific control. I am going to use “FPTInformationSystem Dropdown” and refer to the below link to know more details for this control.

https://github.com/FPTInformationSystem/Dropdown-Xamarin-iOS

Without much introduction, we will skip into the coding part of this article.

Coding Part

Here, I will explain the steps to create Dropdown in Xamarin.Forms.

Step 1 - Creating new Xamarin.Forms Projects.

Step 2 - Creating a Dropdown view in Xamarin.Forms .NetStandard Project

Step 3 - Wrapping FPT dropdown for Dropdown control in iOS Project.

Step 4 - Implementation of Dropdown & Its demo for iOS Platform.

Step 1: Creating a new Xamarin.Forms Projects

Create New Project by Selecting New -> Project -> Select Xamarin Cross Platform App and Click OK.

Note
Xamarin.Forms version should be greater than 4.5.

Dropdown control in Xamarin.Forms

Then Select Android and iOS Platforms as shown below with Code Sharing Strategy as PCL or .Net Standard and Click OK.

Dropdown control in Xamarin.Forms

Step 2: Creating a Dropdown view in Xamarin.Forms .NetStandard Project

In this step, we will see how to create a dropdown view with required properties.

Create a class named as “Dropdown” and inherit the Dropdown with “Label”. That is Dropdown is child of View.

public class Dropdown :  Label
{
      //...
}

Then we will create the required bindable properties. First, we will see, what are all the properties & events required for dropdown.

  1. ItemsSource – To assign the list of data to be populated in the dropdown.
  2. SelectedIndex – To identify the index of selected values from the ItemsSource.
  3. ItemSelected – Event for performing action when the item is selected in the dropdown.

Create a bindable property for the ItemsSource as shown below.

public static readonly BindableProperty ItemsSourceProperty = BindableProperty.Create(
      propertyName: nameof(ItemsSource),
      returnType: typeof(List<string>),
      declaringType: typeof(List<string>),
      defaultValue: null);

public List<string> ItemsSource
{
    get { return (List<string>)GetValue(ItemsSourceProperty); }
    set { SetValue(ItemsSourceProperty, value); }
}

Create a bindable property for the SelectedIndex as shown below.

public static readonly BindableProperty SelectedIndexProperty = BindableProperty.Create(
      propertyName: nameof(SelectedIndex),
      returnType: typeof(int),
      declaringType: typeof(int),
      defaultValue: -1);

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

Create a custom event ItemSelected for dropdown control and invoke the event as shown below.

public event EventHandler<ItemSelectedEventArgs> ItemSelected;

public void OnItemSelected(int pos)
{
    ItemSelected?.Invoke(this, new ItemSelectedEventArgs() { SelectedIndex = pos });
}

Here, ItemSelectedEventArgs is a child of EventArgs as shown below.

public class ItemSelectedEventArgs : EventArgs
{
    public int SelectedIndex { get; set; }
}

Full Code of Dropdown View

Here, we will see the full code for dropdown view.

namespace XF.Ctrls
{
    public class Dropdown : Label
    {
        public Dropdown()
        {
           Padding = new Thickness(10);
        }

        public static readonly BindableProperty ItemsSourceProperty = BindableProperty.Create(
            propertyName: nameof(ItemsSource),
            returnType: typeof(List<string>),
            declaringType: typeof(List<string>),
            defaultValue: null);

        public List<string> ItemsSource
        {
            get { return (List<string>)GetValue(ItemsSourceProperty); }
            set { SetValue(ItemsSourceProperty, value); }
        }

        public static readonly BindableProperty SelectedIndexProperty = BindableProperty.Create(
            propertyName: nameof(SelectedIndex),
            returnType: typeof(int),
            declaringType: typeof(int),
            defaultValue: -1);

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

        public event EventHandler<ItemSelectedEventArgs> ItemSelected;

        public void OnItemSelected(int pos)
        {
            ItemSelected?.Invoke(this, new ItemSelectedEventArgs() { SelectedIndex = pos });
        }
    }

    public class ItemSelectedEventArgs : EventArgs
    {
        public int SelectedIndex { get; set; }
    }
}

Step 3: Wrapping FPT dropdown for Dropdown control in iOS Project.

In this step, we will see “How to wrap the iOS Spinner Control for Dropdown View”.

We need to download the DLL of FPT dropdown from the below GitHub link and include it as a reference for the iOS project.

Create a class file named as “DropdownRenderer” in your iOS client project and add View Renderer as shown below. I am using Label renderer for displaying the dropdown when the control is tapped.

public class DropdownRenderer :  LabelRenderer
{
      XFDropdown xfDropdown = null;
      XIDropdown dropDown = null;
      public DropdownRenderer()
      {

      }    
      // ...
}

Then Override the methods “OnElementChanged” and “OnElementPropertyChanged”. OnElementChanged method is triggered on element/control initiation. OnElementPropertyChanged method is called when the element property changes.

Set Control properties to LabelRenderer in OnElementChanged override method as shown below.

We need to follow the below for initiating the FPT dropdown and open it when the label is tapped.

if (Control != null)
{
    dropDown = new XIDropdown();
    xfDropdown = (XFDropdown)e.NewElement;
    dropDown.AnchorView = new WeakReference<UIView>(Control);
    nfloat y = dropDown.Bounds.Height;
    if (y == 0)
        y += 40;
    dropDown.TopOffset = new CoreGraphics.CGPoint(0, -y);
    dropDown.BottomOffset = new CoreGraphics.CGPoint(0, y);

    //...

    UITapGestureRecognizer labelTap = new UITapGestureRecognizer(() =>
    {
        dropDown.Show();
    });

    if (xfDropdown.SelectedIndex > -1)
        Control.Text = xfDropdown.ItemsSource[xfDropdown.SelectedIndex];
    Control.UserInteractionEnabled = true;
    Control.AddGestureRecognizer(labelTap);
}

Set Items Source from Xamarin.Forms Dropdown to FPT dropdown control as shown below.

string[] data = xfDropdown.ItemsSource.ToArray();
dropDown.DataSource = data;

Set default selection of item from selected index as shown in below.

if(xfDropdown.SelectedIndex != -1)
{
      dropDown.SelectRow(xfDropdown.SelectedIndex);
}

Create an item selected event for FPT dropdown and invoke the dropdown event created as shown below

// ...

dropDown.SelectionAction = (nint idx, string item) =>
{
      if (xfDropdown.SelectedIndex == idx)
      {
            dropDown.Dispose();
            return;
      }

      xfDropdown.SelectedIndex = Convert.ToInt32(idx);
      Control.Text = item;
      xfDropdown.OnItemSelected(xfDropdown.SelectedIndex);
};

In the same way, we will assign ItemsSource & SelectedIndex to FPT dropdown when the property changes using OnElementPropertyChanged as shown below.

protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
      base.OnElementPropertyChanged(sender, e);
      dropDown.Width = (nfloat)Element.Width;
      if (e.PropertyName == XFDropdown.SelectedIndexProperty.PropertyName)
      {
            if (xfDropdown.SelectedIndex > -1)
                  Control.Text = xfDropdown.ItemsSource[xfDropdown.SelectedIndex];
            dropDown.SelectRow(xfDropdown.SelectedIndex);
      }

      if (e.PropertyName == XFDropdown.ItemsSourceProperty.PropertyName)
      {
            string[] data = xfDropdown.ItemsSource.ToArray();
            dropDown.DataSource = data;
      }
}

Add Export Renderer above the namespace to link dropdown view in .NetStandard project to Android Client Project. This is a very important step for any custom renderer approach.

[assembly: ExportRenderer(typeof(Dropdown), typeof(DropdownRenderer))]
namespace XF.Ctrls.iOS

Full Code of Dropdown Renderer

Here, we will see the full code for Dropdown Renderer.

[assembly: ExportRenderer(typeof(XFDropdown), typeof(DropdownRenderer))]
namespace XF.Ctrls.iOS
{
    public class DropdownRenderer : LabelRenderer
    {

        XFDropdown xfDropdown = null;
        XIDropdown dropDown = null;

        protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
        {
            base.OnElementChanged(e);
            if (Control != null)
            {
                dropDown = new XIDropdown();
                xfDropdown = (XFDropdown)e.NewElement;
                dropDown.AnchorView = new WeakReference<UIView>(Control);
                nfloat y = dropDown.Bounds.Height;
                if (y == 0)
                    y += 40;

                dropDown.TopOffset = new CoreGraphics.CGPoint(0, -y);
                dropDown.BottomOffset = new CoreGraphics.CGPoint(0, y);
                string[] data = xfDropdown.ItemsSource.ToArray();
                dropDown.DataSource = data;
                Control.Text = data[0];
                dropDown.SelectionAction = (nint idx, string item) =>
                {
                    if (xfDropdown.SelectedIndex == idx)
                    {
                        dropDown.Dispose();
                        return;
                    }
                    xfDropdown.SelectedIndex = Convert.ToInt32(idx);
                    Control.Text = item;
                    xfDropdown.OnItemSelected(xfDropdown.SelectedIndex);
                };

                UITapGestureRecognizer labelTap = new UITapGestureRecognizer(() =>
                {
                    dropDown.Show();
                });

                if (xfDropdown.SelectedIndex > -1)
                    Control.Text = xfDropdown.ItemsSource[xfDropdown.SelectedIndex];
                Control.UserInteractionEnabled = true;
                Control.AddGestureRecognizer(labelTap);
            }
        }

        protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);
            dropDown.Width = (nfloat)Element.Width;
            if (e.PropertyName == XFDropdown.SelectedIndexProperty.PropertyName)
            {
                if (xfDropdown.SelectedIndex > -1)
                    Control.Text = xfDropdown.ItemsSource[xfDropdown.SelectedIndex];
                dropDown.SelectRow(xfDropdown.SelectedIndex);
            }
            if (e.PropertyName == XFDropdown.ItemsSourceProperty.PropertyName)
            {
                string[] data = xfDropdown.ItemsSource.ToArray();
                dropDown.DataSource = data;
            }
        }
    }
}

Step 4: Implementation of Dropdown & It’s Demo for iOS Platform

In this step, we will see how to use the view in Xamarin.Forms.

Open your designer file and in my case MainPage.xaml and add the control as shown below.

<local:Dropdown HorizontalOptions="FillAndExpand"
                VerticalOptions="Center"
                BackgroundColor="LawnGreen"
                x:Name="dropdown"/>

Set ItemsSource and SelectedIndex as shown below.

dropdown.ItemsSource = Items1;
dropdown.SelectedIndex = 1;

Add item selection event to dropdown as shown in below.

public MainPage()
{
    //...
    dropdown.ItemSelected += OnDropdownSelected;
}

private void OnDropdownSelected(object sender, ItemSelectedEventArgs e)
{
    label.Text = Items1[e.SelectedIndex];
}

Full Code implementation of Dropdown in MainPage

Here, we will see the full code for Main Page.

namespace XF.Ctrls
{
    public partial class MainPage : ContentPage
    {
        List<string> Items1 = new List<string>();
        List<string> Items2 = new List<string>();
        bool IsItem1 = true;

        public MainPage()
        {
            InitializeComponent();

            for (int i = 0; i < 4; i++)
            {
                Items1.Add(i.ToString());
            }

            for (int i = 0; i < 10; i++)
            {
                Items2.Add(i.ToString());
            }

            dropdown.ItemsSource = Items1;
            dropdown.SelectedIndex = 1;
            dropdown.ItemSelected += OnDropdownSelected;
        }

        private void OnDropdownSelected(object sender, ItemSelectedEventArgs e)
        {
            label.Text = IsItem1 ? Items1[e.SelectedIndex] : Items2[e.SelectedIndex];
        }

        private void btn_Clicked(object sender, EventArgs e)
        {
            dropdown.ItemsSource = IsItem1 ? Items2 : Items1;
            dropdown.SelectedIndex = IsItem1 ? 5 : 1;
            IsItem1 = !IsItem1;
        }
    }
}

Demo

The following screens shows the output of this tutorial and it is awesome to have this dropdown in Xamarin.Forms.

Platform

 

Screenshots

 

Android

 

 

 

iOS

 

 

 

This article covers the implementation of new dropdown control in the iOS Platform. Refer to my previous article for the implementation of Dropdown in the Android Platform.

My plan is to create a dropdown control for supporting all platforms and offering this control as a standard plugin.

Download Code

You can download the full source code from GitHub. If you like this article, do like, share and star the repo in GitHub.