A technology-agnostic blog about technology.

Monotouch tip: Inherit UIViews ALL the time!

By ESCOZ posted January 13th, 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.


14 Comments on “Monotouch tip: Inherit UIViews ALL the time!”

  1. 1 MonoTouch.Info said at 10:33 am on January 13th, 2010:

    Monotouch tip: Inherit UIViews ALL the time!…

    Thank you for submitting this entry – Trackback from MonoTouch.Info…

  2. 2 ESCOZ » Blog Archive » Editing decimals and currencies with UITextField control with MonoTouch said at 2:06 pm on January 16th, 2010:

    [...] user to edit decimal values using the NumberPad keyboard, instead of the normal keyboard. Again, as I mentioned in my previous post, inheritance provides a much better way of adding functionality to existing UIKit controls, instead [...]

  3. 3 Tweets that mention ESCOZ » Blog Archive » Monotouch tip: Inherit UIViews ALL the time! -- Topsy.com said at 5:49 pm on January 17th, 2010:

    [...] This post was mentioned on Twitter by Akihiro Uehara, Akihiro Uehara. Akihiro Uehara said: 雑感:MonoTouchのフォーラムやこのBlogなど、実務で使い込みこなれた人が沢山出てきた。 http://escoz.com/blog/monotouch-tip-inherit-uiviews-all-the-time/ [...]

    [WORDPRESS HASHCASH] The comment’s server IP (208.74.66.43) doesn’t match the comment’s URL host IP (74.112.128.10) and so is spam.

  4. 4 Debbie said at 9:30 am on January 24th, 2010:

    Any chance of posting a downloadable app. I’m having difficult following exactly how this works.

  5. 5 ESCOZ said at 8:03 am on January 25th, 2010:

    Hey Debbie, I’ll try to post an sample app in the next day or so.
    Is there anything specific you’re having problems with, or would like to know more about?

  6. 6 John said at 8:43 pm on February 17th, 2010:

    Hey Eduardo,

    Any chance you have a demo app that I can download or screenshots?

    Particularly around “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.”

    Cheers.

  7. 7 John said at 9:30 pm on February 19th, 2010:

    Any chance you’ve made the sample app available for download yet?

    Thanks.

  8. 8 Debbie said at 10:03 am on February 21st, 2010:

    Sorry. I forgotten to check back on this.

    I think my problem is that I’m not entirely sure how it all fits together. Kind of an “I don’t know, what I don’t know” situtation.

    I know that’s pretty unhelpful, hence my thought that review a complete sample app might get me oriented.

  9. 9 Debbie said at 10:49 am on February 21st, 2010:

    Just looked at this in detail again. I think my first area of confusion is similar to John’s above – i.e. where you start explaining the “hands on” stuff.

    If I create a UIView in IB and then save it, surely MonoTouch won’t create new classes for it – unless there was a preceding step where I’d created the view in a MonoTouch project, or something. I think, for starters, I’m missing something there.

    Then I’m confused about how to create a controller than will consume the events I create using IB, as you discuss in the penultimate paragraph.

    Finally, I’m not all that clear on how the controller will be linked with the view (although that might become clear when the other bits are clear).

    Hence I think a working sample might answer some of these questions about how it all hangs together. Possibly a simple project with two views?

  10. 10 Debbie said at 2:57 pm on February 21st, 2010:

    Another question, I’m afraid. I’m confused about different controllers too, I think. If I use a UITabBarController within IB, would that really be internal to a “view” (in terms of MonoTouch) – i.e. it has nothing to do with the “MVC” type of controller that you’re talking about creating a class for in MonoTouch? So, in effect, I could refer to it a UITabBarManager, or something – just to clarify things in my own mind?

  11. 11 John said at 8:47 pm on February 27th, 2010:

    Debbie,

    I’ve been mucking around with Mono Touch a little more over the last couple of weeks and I’ve got something working pretty closely to what Eduardo speaks of here (or at least I think so).

    Q: “If I create a UIView in IB and then save it, surely MonoTouch won’t create new classes for it – unless there was a preceding step where I’d created the view in a MonoTouch project, or something. I think, for starters, I’m missing something there.”
    A: If I had to do it like how it’s done in this example. I’d create a UserView View Interface Definition in Mono Touch. When I double click the view it launches the IB. I drag the items I need into the View and I then go to the Inspector and and rename the UIView to UserView in the Class Inspector.

    I then go to the File Owner and add a view outlet and hook that up to my UserView. I also go about adding outlets for my buttons and labels in my UserView View and hook them up. When I save this, I notice that IB creates the partial class just the way I want it (private fields in the UserView partial class). All is well.

    Q: “Then I’m confused about how to create a controller than will consume the events I create using IB, as you discuss in the penultimate paragraph.”
    A: For the controller I’d add a new class to my MonoTouch project and in this instance it’ll be UserViewController.cs. Here I inherit from UIViewController. Once that happens I override the ViewDidLoad method and I add the grits of what I need done at this point. Including dynamically loading the nib (see below).

    Q: “Finally, I’m not all that clear on how the controller will be linked with the view (although that might become clear when the other bits are clear).”
    A: This bit took me a day and a half of mucking around before I got it right. What I’m doing here is, loading the Nib on the fly (in this case UserView) by doing:
    var views = NSBundle.MainBundle.LoadNib(“UserView”, this, null);
    UserView view = new UserView(views.ValueAt(0));

    This fires up the UserView and all is well again. You should now be able to access your view from your controller.

    This has taken me some trial and error but it works and I can get things done fairly easily through this. There’s no doubt in my mind that there’s probably an easier/better way to do this but this has gotten the ball rolling for me. I hope this helps you too.

    Cheers.

  12. 12 Fred Zarguna said at 4:12 pm on April 17th, 2010:

    What John is doing is, in general, the best way to get this to work. In some ways, MonoTouch (and IB’s) insistence on connecting views w/ controllers is misleading. There is often no real good reason (other than convenience: which is a good reason for COBOL programmers, but not for real coders) to define the Controller in IB. In fact, for large projects it is completely unhelpful.

    As John outlines in the preceding post, if you want to create your UIView subclass visually, create it as a View Only project — not as a View w/ Controller. The only thing you will need to be careful of: if you load your view using NSBundle…LoadNib you will not really have a useful ViewDidLoad() unless you trigger that yourself. It’s easier and more reliable to access the components within your dynamically loaded view by using ViewWillAppear(). Many of the components will not even exist right after loading them from the x/nib.

    If you really want to do inheritance, the best way to do it is as outlined here. The truth is, IB doesn’t really support visual inheritance — but it is willing to ignore it, mostly. In which case, the best thing to do is make your controllers *non-visual*: don’t let IB anywhere near them. Then you can inherit the non-visual parts of your code, at the very least.

  13. 13 david said at 4:23 am on April 28th, 2010:

    Hi Eduardo,

    one stupid question, how do you instantiate the control, from code. Not sure what i must pass in the constructor:

    UserView (IntPtr p)

    what is p?.

  14. 14 Stephen said at 3:36 am on July 8th, 2010:

    Any chance of a sample project (as offered)? I’m finding it a little difficult to follow the article.


Leave a Reply

  • Powered by WP Hashcash