Monotouch tip: Inherit UIViews ALL the time!

Posted by ESCOZ on Wednesday, January 13, 2010

When I started developing with MonoTouch, I ended up reading lots of tutorials, and watching lots of YouTube screencasts.

While those were definitely useful to get started on the platform, the large majority of them seem to implement things in a way that simply doesn’t scale very well: by adding outlets of every control to the AppDelegate class or to a ViewController, in Interface Builder. While that may work right in a simple HelloWorld app, it simply doesn’t scale well. It’ll become impossible to manage the application after you add more than a couple of views and controls.

Inheriting from UIView (or UIScrollView), is a much better way of creating views for your application. Interface Builder can still be used to design the UI, while the coding of the behavior can be done in MonoDevelop. Doing this will make the code much cleaner, by allowing you to separate different concerns across different classe: ui-related functions, like formatting of the text or the selection of the background color can be done in the View class, while definition of what’s loaded, and calling web services can be done in the Controller.

To do this, first go to Interface Builder, and build your UIView with some labels as normal. Now, go to the Library, select “Classes” from the top selector, and find the UIView class. Right click on it, and select “Add subclass”. Type in the name of your new view, like UserView, and click ok. Finally, select the view you created previously, go to the Object Identity field, and change the class to the new subclass.

After you save the Interface Builder file, MonoTouch will automatically generate a partial class for your new view, like below:

 
// Base type probably should be MonoTouch.UIKit.UIView or subclass
[MonoTouch.Foundation.Register("UserView")]
public partial class UserView{
}

By itself, that is not very useful. But the difference is that now you can create outlets to the UILabels in this new view, instead of in the controller! Doing that would generate code like this:

 
// Base type probably should be MonoTouch.UIKit.UIView or subclass
[MonoTouch.Foundation.Register("UserView")]
public partial class UserView{
    [MonoTouch.Foundation.Connect("Key")]
    private MonoTouch.UIKit.UILabel NameLabel {
        get {
            return ((MonoTouch.UIKit.UILabel)(this.GetNativeField("NameLabel")));
        }
        set {
            this.SetNativeField("NameLabel", value);
        }
    }
}

Notice how the property created is defined as private. It’s great that MonoTouch defaults to private, as it forces you to define a public method for the view, which will likely have more to do with the application domain, instead of just calling the labels from the controller.

Finally, you’ll need to create the other side of the partial, with the real implementation of the class, and constructors. For that, create a new file in MonoDevelop called UserView.cs, like below:

 
public partial class UserView : UIView {

    public UserView (IntPtr p) : base (p) {
    }
    
    public void DisplayUser(User user) {
        NameLabel.Text = user.Name;
    }
}

You’ll need to inherit your class from UIView, like you defined in Interface Builder, and implement a default constructor that receives an IntPtr object, passing that directly to the UIView’s constructor. This constructor is used by MonoTouch to instantiate the object when it’s deserializing the view from the NIB file. I also included a simple method called “DisplayUser()”, just to give a rough idea on what you can do here. Your views will probably have much more complicated logic here.

Lastly, your views will receive events from the User, and you’ll need to pass that to your controllers. Personally, I prefer to user Interface Builder to create events directly in the controller, and just hookup the events directly. The controller in C# would then implement that event, and be responsible for getting the data from the view. For complicated views, it might be useful to also create the event directly in the view class.

That’s it! The view code is now totally separate from the AppDelegate or any controllers. While this seems overkill for such a simple example, for larger applications this will enormously increase the quality of your code.