Taming the user interface on iPhone with MonoTouch.Dialog

Original author: Miguel de Icaza
  • Transfer
monotouchThe user interface is based on UITableView, a powerful table rendering widget that uses almost every iPhone application. UITableView is a powerful widget that can render data in various ways, based on how you configured the widget itself.

Here is an example of all kinds of UITableViews:


The contents of a UITableView are rendered by calling code written by a developer who provides data on demand. The protocol includes such requests as: “How many sections?”, “How many lines are in section N?”, “What is the title of section N?” in the form of callbacks to provide the actual contents of the cell. Despite the full power of the widget, creating a UI with it is quite problematic. Developers spend too much time repeating their actions, customizing each view, meager configuration and the need to grind some settings. Porting many examples from Object-C to C #, I found the right option by repeating the same process over and over again.

Calluses appeared on my fingers, but even at night I thought that I had found a not perfect solution and there was a better option. But at that time, I was doing simple line-by-line porting, I was not ready to create a new API on top of all this.

Recently, when my favorite twitter client on iPhone debunked my UI, I decided to write my own Twitter client. The first step was to create the settings for my twitter account. As you understand, this is implemented through a UITableView. I had to configure a model that responded to presentation events, the switches and ifs were here and there, in general, no pleasure in writing code. That's how MonoTouch.Dialog was born.

I wanted the reflection to allow the class to bind to the dialog box, something that would allow me to write a C # class and bind it to a UITableView:
class TwitterConfig {
  [Section ("Account")]
   [Entry] string Username;
   [Password] string Password;

  [Section ("Settings")]
   bool AutoRefresh;
   bool AutoLoad;
   bool UseTwitterRetweet;

Instead of starting to use reflection, I created a representation of the current dialogue in memory. The idea was that reflection would be a bridge that could use the engine code.

The engine code is built on the principle that each row can be a widget. It can contain text, a switch, a text field, a slider, a calendar, or any user-created control. I called it “Elements” and created the following:
  • BooleanElement - rendering using UISwitch
  • FloatElement - slider
  • HtmlElement - when clicked, launches a web browser
  • StringElement - displays plain text
  • MultilineElement - multiline text
  • RadioElement - single selection from a list
  • CheckboxElement - like BooleanElement, but uses checkbox instead of UISwitch
  • ImageElement - allows the user to select a picture or take a photo
  • EntryElement - text field
  • DateTimeElement, DateElement, TimeElement - select date / dates and time

MonoTouch.Dialog follows the recommendations of Apple HIG for iPhone, giving you the opportunity to focus as much as possible on the application, and not on its trifles.

Also, UITableView is built on the basis of MVC, which allows you to effectively scale large data sets; most pages with settings and data do not require such complexity.

Another possibility is to solve all problems with entering text: entering text, automatically moving to the next line when you press Enter, aligning all lines in the section, hiding the keyboard when reaching the end of the input.

Example API in action:

imagevar root = new RootElement ("Settings") {
 new Section (){
  new BooleanElement ("Airplane Mode", false),
 new RootElement ("Notifications", 0, 0) { Notifications }
 new Section (){
  new RootElement ("Sound"), { Sound },
  new RootElement ("Brightness"){ Brightness },
  new RootElement ("Wallpaper"){ Wallpaper }
 new Section () {
  new EntryElement ("Login", "Your login name", "miguel"),
  new EntryElement ("Password", "Your password", "password", true),
  new DateElement ("Select Date", DateTime.Now),
  new TimeElement ("Select Time", DateTime.Now),

By creating a RootElement, you can pass it to the DialogViewController for control:
var dv = new DialogViewController (root);
navigation.PushViewController (dv, true);

Reflection API

The replication API checks the class for fields to which special attributes are bound.

An example of a class and how it is rendered:

imageclass AccountInfo {
  public bool AirplaneMode;

  [Section ("Data Entry", "Your credentials")]

  [Entry ("Enter your login name")]
  public string Login;

  [Caption ("Password"), Password ("Enter your password")]
  public string passwd;

  [Section ("Travel options")]
  public SeatPreference preference;

As you noticed, the SeatPreference is automatically converted to radio, which uses the UINavigationController to control, and the headers are taken from the field names, this behavior can be configured using the [Caption] attribute.

Using attributes, you can specify the rendering methods, title, image, etc.

LINQ and MonoTouch.Dialog

Craig wrote a great conference application for Mix 2010. I helped him reduce the amount of code by removing all the duplicate code to set up a UITableView for various parts of the application for MonoTouch.Dialog. Since the conference application works with the schedule in the database, I expanded MonoTouch.Dialog to improve LINQ.

In the same spirit as with the System.Xml.Linq API, which allows you to create XML documents with embedded LINQ definitions, you can use MonoTouch.Dialog to create a UI.

For Craig’s application, I wrote a SessionElement that allows you to start sessions and show the title and location of the session.

The following code contains the UI from the “My Schedule” tab. Data is requested on demand (Apple recommends lazy loading for all views)
imagepublic class FavoritesViewController : DialogViewController {
 public FavoritesViewController () : base (null) { }

 public override void ViewWillAppear (bool animated)
  var favs = AppDelegate.UserData.GetFavoriteCodes();
  Root = new RootElement ("Favorites") {
   from s in AppDelegate.ConferenceData.Sessions
    where favs.Contains(s.Code)
    group s by s.Start into g
    orderby g.Key
    select new Section (MakeCaption ("", g.Key)) {
     from hs in g
      select (Element) new SessionElement (hs)

So use either of the two models that you like best: Reflection for quick and easy work with the interface and data, or Element API for more advanced customization of the user interface, without spending half your life writing boilerplate code.

I hope all this helped you guys spend more time improving their applications and less time writing routine code.

MonoTouch.Dialog is not perfect and does not contain all the wishes. I also welcome additions, you are not shackled by anything in this code branch and make any changes that seem necessary to you.

Also popular now: