Sep 192012
 

I’ve spent the last couple of months getting myself immersed in the world of developing apps for iOS.  My initial venture into this space was doing a small proof-of-concept app using Objective-C.  Coming from a .Net background, this was a less than pleasing experience.  Fortunately for me, the client I’m working for wasn’t excited about the prospect of maintaining an Objective-C codebase since they’re a .Net shop.  This prompted us to look at MonoTouch.  So for we’ve been pretty happy with MonoTouch and have even had a couple of production releases.

Even though with MonoTouch we are coding in a familiar language, there is still a learning curve.  For me, one of the more challenging aspects has been around usability.  Having done web and Windows development for some years now, I can generally look at a user story and determine how a user interface should be composed.  In the iOS world, interactions between your app and users are obviously quite different than in a traditional web or thick client application.  One of the consequences of this is that in iOS, we don’t have the same set of user interface controls to work with that we might be used to in the web or Windows world.  A recent feature I was working on called for a user to provide input into a data entry field via a list of possible values.  In other words, a combobox.  Unfortunately, iOS doesn’t have a direct analog to the classic combobox.  It does however have the UIPickerView which presents a list of possible values that the user can scroll through and select from.  If you haven’t seen the UIPickerView in action before, it looks sort of like a wheel with a bunch of values on it you can scroll through:

When I considered the UIPickerView for my particular requirement, it didn’t seem ideal.  My main issue was that dropping this big honkin’ control on my form was going to take up a lot of real estate.  It just didn’t seem reasonable to display this thing all the time even though the user would really only interact with it for a very short time, when they were selecting a value.  I considered the possibility of triggering the display of the UIPickerView from touch in a textbox or something similar. Again, this didn’t seem ideal.  A bit of digging through Apple’s SDK documentation yielded something interesting.  The UITextField class has a property called InputView.  Setting this property to some view will tell your app to use this custom view to handle input rather than the standard keyboard.  I coded up a quick UIPickerView and assigned it to the InputView property on a UITextField.  Sure enough, when I tapped on the UITextField, my UIPickerView was displayed rather than the keyboard.

I was on my way to a good solution but still had one problem.  Once a user had selected a value from my UIPickerView, I needed to dismiss the UIPickerView.  Since all interactions an iOS app are via touch, there is no way for a UIPickerView to distinguish between a user tapping on the item they wish to select and any other touch interaction the user has with the UIPickerView, such as scrolling through the list.  This means we need some way of telling the UIPickerView that we’ve made our selection and it can get out of our way.  I noticed another interesting property on the UITextField class, InputAccessoryView.  It turns out that if a view is assigned to this property, it will be displayed above the view specified by the InputView property.  With that in mind, I created a UIToolBar, added a button and set that as the InputAccessorView.  I now had a pretty good analog to a combobx that would allow a user to scroll through a list of values and make a selection.

The code to get all this working is pretty simple.  To bind data to a UIPickerView, we need to subclass UIPickerViewModel.  This is what I came up with:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class PickerModel : UIPickerViewModel
{
	private readonly IList values;
 
	public event EventHandler PickerChanged;
 
	public PickerModel(IList values)
	{
		this.values = values;
	}
 
	public override int GetComponentCount (UIPickerView picker)
	{
		return 1;
	}
 
	public override int GetRowsInComponent (UIPickerView picker, int component)
	{
		return values.Count;
	}
 
	public override string GetTitle (UIPickerView picker, int row, int component)
	{
		return values[row];
	}
 
	public override float GetRowHeight (UIPickerView picker, int component)
	{
		return 40f;
	}
 
	public override void Selected (UIPickerView picker, int row, int component)
	{
		if (this.PickerChanged != null)
		{
			this.PickerChanged(this, new PickerChangedEventArgs{SelectedValue = values[row]});
		}
	}
}

A couple of things are worth noting here. First, I’m passing in an IListinto the constructor. This is the list of values that will be displayed in the UIPickerView. Second, I have an event in here that gets raised when the selection changes.

The only other thing of significance is actually setting up the UIPickerView, UIToolbar and binding those to the UITextField:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
private void SetupPicker()
{
	// Setup the picker and model
	PickerModel model = new PickerModel(this.colors);
	model.PickerChanged += (sender, e) => {
		this.selectedColor = e.SelectedValue;
	};
 
	UIPickerView picker = new UIPickerView();
	picker.ShowSelectionIndicator = true;
	picker.Model = model;
 
	// Setup the toolbar
	UIToolbar toolbar = new UIToolbar();
	toolbar.BarStyle = UIBarStyle.Black;
	toolbar.Translucent = true;
	toolbar.SizeToFit();
 
	// Create a 'done' button for the toolbar and add it to the toolbar
	UIBarButtonItem doneButton = new UIBarButtonItem("Done", UIBarButtonItemStyle.Done,
	                                                 (s, e) => {
		this.ColorTextField.Text = selectedColor;
		this.ColorTextField.ResignFirstResponder();
	});
	toolbar.SetItems(new UIBarButtonItem[]{doneButton}, true);
 
	// Tell the textbox to use the picker for input
	this.ColorTextField.InputView = picker;
 
	// Display the toolbar over the pickers
	this.ColorTextField.InputAccessoryView = toolbar;
}

This function gets called in the ViewController’s ViewDidLoad method.

Source code for this post can be found here:  https://github.com/13daysaweek/MonoTouchUIPickerView.git

  8 Responses to “Combobox Type Input With iOS and MonoTouch”

  1. [...] other common issue is how to make enum editor in keyboard like dialog. Christopher House made a blog post on how to setup UIPickerView in TextFiled InputView. All i’ve done is converting his code to [...]

  2. Thanks very much,

    This has helped me a lot in setting up my screen

  3. […] I found this post, where it talks about adding your own custom view for display when the user attempts to add input […]

  4. hello Sir

    its a very cool and great example but if we pull our values from database then how we can bind its VALUE ?

  5. Hello thank you for this post, even it is from 2012 it has helped me now, I just have this issue where the Done button displays on the far left of the toolbar, do you know if it can be moved to the right side?

    • I think you should be able to right align it by first adding a UIBarButtonItem of type UIBarButtonSystemItem.FlexibleSpace and then adding the Done button item.

      • Awesome thanks! for the record I added this before line 25:

        var spacer = new UIBarButtonItem(UIBarButtonSystemItem.FlexibleSpace) { Width = 50 };

        And then included spacer on line 25:
        toolbar.SetItems (new UIBarButtonItem[]{spacer, doneButton},true);

Leave a Reply