Combobox Type Input With iOS and MonoTouch

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

publicclass PickerModel : UIPickerViewModel {privatereadonly IList values; publicevent EventHandler PickerChanged; public PickerModel(IList values){this.values= values;} publicoverrideint GetComponentCount (UIPickerView picker){return1;} publicoverrideint GetRowsInComponent (UIPickerView picker, int component){return values.Count;} publicoverridestring GetTitle (UIPickerView picker, int row, int component){return values[row];} publicoverridefloat GetRowHeight (UIPickerView picker, int component){return 40f;} publicoverridevoid 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

privatevoid 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 inputthis.ColorTextField.InputView= picker; // Display the toolbar over the pickersthis.ColorTextField.InputAccessoryView= toolbar;}

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

Source code for this post can be found here:

comments powered by Disqus