Loading views dynamically from a xib in Xamarin.iOS

When I first started doing MonoTouch development (later to become Xamarin.iOS), the ability to use XCode's Interface Builder (IB) was at the time a broken feature with MonoDevelop. Further, the framework that I was using at work for writing apps had it's own UI abstraction API, so I didn't ever have the opportunity to use IB. I didn't need to...though I really wanted to.

Even though I had been using MonoTouch since early 2011, it was only last year (2013) that I got to use Xcode's IB for the first time on a project at work. And let...me...tell...you......it is awesome. Given that all I'd ever known was programmatically creating iOS view hierarchies in code using the UIKit API...it was like the clouds opened up, beams of sunlight softly struck my face, and choirs of angels were singing hallelujah. That really happened.

I was soon not only creating the usual UIViewControllers and UIViews in XCode, but also UICollectionViewCells, UITableViewCells, etc. On that project I was working on, there was a particular screen I was working on: a notification screen. This was just your typical scrolling screen of items, each one the width of the entire screen. A UITableView would probably do just fine ere, but I personally prefer using UICollectionViews. They just feel more natural to me in that it's a visual collection that I arrange any which way I like, unlike UITableView's prescribed single-column tabular format.

Anyway, as it turns out, the design for this notification screen called for some items in the UICollectionView that were taller than others. In other words, varying heights. At first, a big face-palm was had. "How the hell am I gonna do this??" As it turns out, the whole bit about varying cell heights isn't that big of a deal. (Caveat: that article may need to be updated. I've learned a lot more about UICollectionView since then.)

But then it got me to thinking: since I'm designing these UICollectionViewCells in Xcode's Interface Builder, couldn't I just have a few different custom cells that I've built in IB and load then dynamically as I need them??

As it turns out, the answer is a big fat YES. Cutting to the chase, here's the single line of code that you need to load it:

var mySuperAwesomeCustomView = Runtime.GetNSObject(NSBundle.MainBundle.LoadNib("YourSuperAwesomeCustomView", this, null).ValueAt(0)) as YourSuperAwesomeCustomView;

...where YourSuperAwesomeCustomView corresponds to a xib file in the bundle with the name YourSuperAwesomeCustomView.xib. I keep mine in a "Views" folder in my solution, and notice how I didn't even need to provide a path to the xib; it just magically finds it in the app bundle. Note that at the end, the "as" is casting the returned NSObject to your custom UIView type. At the top of your YourSuperAwesomeCustomView.cs, you must also register your view:

[Register("YourSuperAwesomeCustomView")]
public partial class YourSuperAwesomeCustomView : UIView
{
    ...
}

You're all set! Now you can call that first snippet of code wherever you see fit to load your view. The places where it makes the most sense are, for example, in the GetCell() method of a UICollectionViewController or UITableViewController, or perhaps in the ViewDidLoad() or ViewDidAppear() methods of any of your standard UIViewControllers.

An important thing to remember here is that you're not calling any kind of constructor when you're dynamically loading these views, so you won't be able to pass any data to it through the constructor. Generally, you'll have a bunch of properties on your custom view (covered in another post, not yet written as of 4/11/14), each of which represents some detailed aspect of your custom view: UILabels, UITextViews, UIImageViews, etc. In other words,"Controls", for you folks coming from WPF land. Once you have an instance of your custom view via the LoadFromNib() and GetNSObject() methods mentioned above, you can just assign and tweak those properties to your heart's content.

Hope this helps someone!

comments powered by Disqus