Using a UISplitViewController to Create a Master-Detail iPad App with MonoTouch

The UISplitViewController is a new controller that is designed for the iPad to create and manage two separate views (and their controllers) side-by-side of each other. This lends itself well to a master-detail scenario, where the master list in kept on the left and the detail view fills the remaining space on the right, when in landscape orientation. In portrait the left-hand view is hidden and can be presented using the new UIPopoverController in coordination with the UISplitViewControllerDelegate. I’ll touch on some of the details of this here and then provide a basic working sample to get you started.

In MonoDevelop you can create an iPad app using the iPad Window-based project template. After creating a new app, open up MainWindow.xib and add a UISplitViewController. The default UISplitViewController adds UINavigationController containing a UITableViewController on the left side of the split view and a UIViewController on the right. The table will contain the master list. When you select a row, the detail view will update on the right.

Programming the table is identical to the way you do it on the iPhone. Add a class to the project in MonoDevelop with all the code for a UITableViewController subclass (decorated with the Register attribute ) and then in Interface Builder, you can set the class of the UITableViewController in the Identity Inspector as usual (I named it MasterTableViewController). For the detail view, we’ll load it from a separate xib. Create a new View Interface with Controller called DetailViewController and then, back in Interface Builder, you can set the class of the right-hand view controller to DetailViewController and the nib name to DetailViewController. The default template for the DetailViewController.xib will be that of an iPhone at the time of this writing. To make it look like an iPad at design-time, before adding anything to the contained view, you can use File>Create iPad version in Interface Builder to create a copy with the iPad template and overwite the existing iPhone one. Then you can open the new xib in Interface Builder and the designer will be sized for an iPad. I added a label to the detail view which I updated with the text of the row selected in the table along with the outlet for it in the DetailViewController. Also in the DetailViewController’s view, I added a UINavgationBar to the top, also with an outlet.

Here’s what Interface Builder looks like for the MainWindow.xib when complete:

and here is the DetailViewController.xib (there is a label UILabel in the center of the View with the text removed):

When you rotate between landscape and portrait orientations, the left (master) view will be shown and hidden respectively. A button is added to the UINavigationBar in portrait orientation in order to present the master view in the new UIPopoverController. The button and the UIPopoverController are both passed into the UISplitViewControllerDelegate when the left view will be hidden (when entering portrait). As the orientation changes you can manage adding and removing the
button and the existence of the popover by subclassing WillHideViewController and WillShowViewController.


public class SplitDelegate : UISplitViewControllerDelegate
{

public override void WillHideViewController (UISplitViewController svc, UIViewController aViewController, UIBarButtonItem barButtonItem, UIPopoverController pc)
{
DetailViewController dvc = svc.ViewControllers[1] as DetailViewController;

if (dvc != null) {
dvc.AddNavBarButton (barButtonItem);
dvc.Popover = pc;
}
}

public override void WillShowViewController (UISplitViewController svc, UIViewController aViewController, UIBarButtonItem button)
{
DetailViewController dvc = svc.ViewControllers[1] as DetailViewController;

if (dvc != null) {
dvc.RemoveNavBarButton ();
dvc.Popover = null;
}
}

}

The DetailViewController has the implementation to add and remove the button:


...
public void AddNavBarButton (UIBarButtonItem button)
{
button.Title = "Master List";
navBar.TopItem.SetLeftBarButtonItem (button, false);
}

public void RemoveNavBarButton ()
{
navBar.TopItem.SetLeftBarButtonItem (null, false);
}

The popover is dismissed when you touch outside its area. I also programatically dismiss it after a cell is selected (when in portait orientation) within the DetailViewController’s implementation when the detail data (a string in this simple example) is set.


public partial class DetailViewController : UIViewController
{
UIPopoverController _pc;
string _detail;

public UIPopoverController Popover {
get { return _pc; }
set { _pc = value; }
}

public string Detail {
get { return _detail; }
set {
_detail = value;

if (_pc != null) {
_pc.Dismiss (true);
}

detailLabel.Text = _detail;
}
}
...

One final note, you need to override ShouldAutorotateToInterfaceOrientation and return true for both the DetailViewController and the MasterViewController so that the UISplitViewController can make orientation changes to the ui appropriately, even thought the MasterViewController is not displayed in portrait mode other than in the popover.

The final running app looks like this:

The code for the sample project is available here:

25 thoughts on “Using a UISplitViewController to Create a Master-Detail iPad App with MonoTouch

  1. Pingback: MonoTouch.Info
  2. Hi Mike,

    Thanks for the great tutorial.

    BTW, one problem I encountered when running the demo on the iPad in Landscape mode was that the app starts in Portrait mode and then switches to Landscape. Is there a way to start the app in the current orientation that the iPad is in.

    Thx,
    Ash

  3. Mike,

    Its a great tutorial, thanks for sharing.

    Is there a way, you can programatically switch to a different detail window for an action on a different detail window (say clicking a button on page 10, going to page 3) ?

    regards,
    Rajesh.

  4. Hi Mike, great tutorial. How excatly I could add more details on the DetailViewController. For example, the right UITableViewController would contain a list of projects and the details view would contain a UIMap, UIImageView, and a few labels for each selected project?

  5. When I try to run it on my iPad it starts the open animation but immediately closes. It works in the iPad emulator but not on the device.

    I have been able to load other demo apps on this iPad so I’m not sure what the issue is at this point.

    • Hi Paul,

      I just tried the demo app on a device here and couldn’t get it to fail. Upon inspection of the code it looks like the instance of SplitDelegate I created in AppDelegate.FinishedLaunching could be gc’d. Try keeping a reference to it like this and see if that helps:

      public partial class AppDelegate : UIApplicationDelegate
      {
      SplitDelegate _sd;

      public override bool FinishedLaunching (UIApplication app, NSDictionary options)
      {
      _sd = new SplitDelegate ();
      splitViewController.Delegate = _sd;

      Also, try looking in the Xcode Organizer with the device connected for additional info.

  6. Hi, Is is possible to have a main window with navigation controller and a button on it, that when clicked, takes you to a split view controller view like the one you have detailed here?
    Thanks.

    • I haven’t tried this myself, but you should be able to add the splitview controller to a nav controller’s stack just like any other controller.

      • I’ve been trying but all my attempts fail… I have no idea why.. I just get the table view taking up the entire screen and the rotation doesn’t work..

      • Actually, according to Apple’s documentation, the UISplitViewController is always supposed to be the root view controller.

  7. Mike, great tutorial and neat code!
    I am trying to understand why did you create the SplitDelegate class? And also if you can explain the meaning of following line of code and your logic behind it that will be great

    splitViewController.Delegate = new SplitDelegate ();

    thanks again.

    • Thanks.

      The Delegate property of a controller in Objective-C points to the Delegate for the controller, which is a class that implements the particular Objective-C protocol. Protocols in Objective-C are similar to interfaces in C# except protocols can have optional methods. Therefore, MonoTouch binds these to classes in C#. To adopt a protocol for a Delegate in C#, you subclass the class that MonoTouch has bound to the protocol. Since the Delegate (not to be confused with a C# delegate) is associated with a controller, it is common to implement the class as a nested class on the controller for which it is a Delegate. In Objective-C you’ll typically find the protocol for a Delegate adopted in the class for which it is a Delegate, so nesting in C# is about as close as you can get to this. (It’s also worth noting that the MonoTouch team has been exposing many of the callback methods found in Delegates via C# events to make the code more .Net-like). Once, you assign the Delegate instance to the controller, the controller can then send a variety of callback methods to the class that implements the Delegate.

      Cheers,
      Mike

    • That’s setting up the master view controller with a pointer to the detail view controller so that changes in the master’s table can affect changes in the detail view. In general it’s not a good approach to tightly couple view controller’s like this, but in the case of a master detail scenario, I thought it was reasonable.

  8. Hi.

    I’m new to iPad Application.. So i need one Basic Sample Project like”HELLO WORLD” .. Can u send you it to my Mail(send2mm@gmail.com).
    Am waiting for your solution so kindly send me as soon as possible..This is my humble request.

    Thanks & Regards,
    Manimaran,V

  9. Hai Mike,
    It’s a great tutorial.. Can u possible to send the guidelines for getting first Screen is Normal View Controller, After that screen i need Split View controller. Is it possible? And Can we use one are more Split View Controllers in single app?

    Thanx and Regards,
    Rajesh…

  10. Hey Mike,

    Is there a way o interchange the places of left and right views ? I mean, could I get master view on the right and detail view on the left ?

    Thanks

  11. Hi Mike,

    the detail section is now connected to DetailViewController

    What is the best way to interact with several detailviewcontrollers?

    If the user selects item 1 it should show detailviewcontroller1
    If the user selects item 2 it should show detailviewcontroller2

    But in IB you can only set up 1 class which is now detalviewcontroller.
    In the spitcontrolwindow it says “launched from detailviewcontroller”

    Can you point me how to set this up within this demo?

    regards

    Richard

  12. Hi,
    I have some problems using a SplitViewController.

    This is my case:
    I have a main viewController that displays a button.
    By clicking on it, I replace the current view by a new one that contains the splitViewController.

    When the device is in landscape mode everything works but in Portrait mode I can’t see the popover button.

    And I can’t return back to my viewController: [self.parentViewController dismissModalViewControllerAnimeted:YES] is not fonctionnal

    Help me.
    Thanks

Leave a reply to mikebluestein Cancel reply