Xamarin.Forms and iOS segemented control

A common control in many iOS apps is the segmented control. Xamarin.Forms has no support for the segemented control out of the box. This blog post will show you how to create a control in Xamarin.Forms that is mapped to UISegmentedControl in iOS.

In my sample I have named the control, FilterControl because is often used as a filter control and if you write a implementation of the control on the other platforms it will maybe not look lite the segmented control.

To make the control usefull I need to be able to bind items to it, bind to selected index, have an event that is thrown when selected index is changed and be able to change the color.

The first I will do is to create a class in the shared project that inherits from View.

If the properties will be bindable you have to create a BindableProperty as shown in the code below.

To set the choises (items) of the control we’re creating a List of strings.

public static readonly BindableProperty ItemsProperty =
			BindableProperty.Create<FilterControl, List<string>>
		(p => p.Items, new List<string>());
 
public List<string> Items 
{
	get 
        {
		return GetValue (ItemsProperty) as List<string>;
	}
	set 
        { 
		SetValue (ItemsProperty, value);
	}
}

When the control is defined next step is to create a renderer for iOS. The renderer will inherit from ViewRenderer.

public class FilterControlRenderer : ViewRenderer<FilterControl, UISegmentedControl>

In the override of the OnElementChanged we will create the native control, UISegmentedControl. I will put the code to map the list of choises to the segment control in a separate private method so I also can use it if the list of choices is changed. Then I will call it from the override of the OnElementPropertyChanged method.

You need to listen to ValueChanged of the UISegmentedControl so you can write the value back to the Xamarin.Forms control. In the event for selection changed I want both the old and new value, there for will I dont set selected index from the renderer. Instead I call a method in the control that is setting SelectedIndex and throwing the selection changed event.

protected override void OnElementChanged (ElementChangedEventArgs<FilterControl> e)
{
	base.OnElementChanged (e);
 
	var segmentControl = new UISegmentedControl ();
        SetNativeControl (segmentControl);
 
	UpdateSegments ();
 
	segmentControl.SelectedSegment = Element.SelectedIndex;
	segmentControl.SizeToFit ();
 
	segmentControl.ValueChanged += (object sender, EventArgs args) => 
	{
		if(segmentControl.SelectedSegment != Element.SelectedIndex)
		{
		     Element.OnSelectedIndexChanged((int)segmentControl.SelectedSegment);
		}
	};
}
 
private void UpdateSegments()
{
	Control.RemoveAllSegments ();
 
	for (int i = 0; i < Element.Items.Count; i++) 
	{
		Control.InsertSegment (Element.Items [i],i,true);
	}
}
public void OnSelectedIndexChanged(int newValue)
{
        var args = new IndexChangedEventArgs () {
		NewValue = newValue,
		OldValue = SelectedIndex
	};
 
	SelectedIndex = newValue;
 
	if (SelectedIndexChanged != null) 
	{				
		SelectedIndexChanged (this, args);
	}
}

Of course we also need to update the native control if the value for selected index is changed. That code will be placed in the OnElementPropertyChanged method.

protected override void OnElementPropertyChanged (object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
        base.OnElementPropertyChanged (sender, e);
 
	if (e.PropertyName == FilterControl.SelectedIndexProperty.PropertyName) 
	{
		if (Control.SelectedSegment != Element.SelectedIndex) 
		{
		         Control.SelectedSegment = Element.SelectedIndex;
		}
	}

The complete code can be found on GitHub, https://github.com/dhindrik/XamarinFormsSamples/tree/master/SegmentedControl

If you want the control to be in the NavigationBar on iOS read this blog post.

2 thoughts on “Xamarin.Forms and iOS segemented control

Leave a Reply

Your email address will not be published. Required fields are marked *