Monotouch tip: Inherit UIViews ALL the time!
By ESCOZ posted January 13th, 2010When 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.
Filed under: Development | Tags: MonoTouch, Redpoint | 11 Comments »
Monotouch tip: Inherit UIViews ALL the time!…
Thank you for submitting this entry – Trackback from MonoTouch.Info…
[...] 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 [...]
[...] 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.
Any chance of posting a downloadable app. I’m having difficult following exactly how this works.
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?
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.
Any chance you’ve made the sample app available for download yet?
Thanks.
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.
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?
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?
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.