[MonoTouch] Startup times: nib vs. nib-less [was Re: Can I show splash image within a second?]

Rory Blyth lists at rory.me
Sat Nov 28 17:04:28 EST 2009


[Typical Rory summary because my emails tend to be so long: Three things are covered here: 1. Loading a tab bar controller from the MainWindow nib while also having a custom splash screen *and* not having it all get all wonked-up, and 2. Differences between "loadView" and "viewDidLoad" - when to use them, what they're for, and 3. When it's a matter of preference to go with "loadView" vs. "viewDidLoad".]


@Ed: Ok, gotcha... that makes total sense.

---- Thing One: Avoiding tab bar controller issues when loading from a nib... ----

What I learned to do at my last job is (and they didn't agree with me - everybody else was very anti-nib (with reason), but I still wanted to try it):

1. Create your tab bar controller in your MainWindow xib.

2. In "applicationDidFinishLaunching" (MT: "FinishedLaunching"), *manually* instantiate a custom UIView subclass that acts as your splash screen. In my case, the splash screen used "default.png" so it looked just like the default load screen (I'll explain why in a second).

3. Add the tab controller's view to your window.

4. Add the splash screen UIView subclass that you manually instantiated to the window. Have it remove itself after a short delay (give it a "GetRidOfMeAfterAQuarterOfASecond" method (named a little better, of course)).

Doing this allowed me to smoothly transition from "default.png" to the app itself. For this app, I was following Apple's guidelines (don't use "default.png" as a way to announce what app is loading, nor to advertise other apps, etc. - use it to make it look like your app is picking up where it left off - we violated this guideline often, but I didn't in this case). The problem is that the default behavior is that jarring transition from "default.png" to your app.

By creating a UIView subclass just for your splash screen (which is perfectly reusable), you can control the transition. With that project, "default.png" was the app and tab bars, but it didn't show which tab was currently selected, nor did it show any content (it's "default.png", so that's normal). But, when the tab controller was loaded and ready, I did a fade, and the result was a much better transition - it feels like the tab bar controller was always there and ready to go rather than being loaded from a nib and then slapped on the screen after "Default.png" gets yanked off (in other words: it was graceful instead of abrupt and jerky). Unfortunately, the app wasn't released (insert long story about client and disagreements and other things that'd get me sued if I talked about them).

Here's the Objective-C code (haven't done it with MT yet, but it'd be just as easy, methinks):

// The get-rid-of-me-method in your splash screen UIView subclass

- (void)fadeOutAfterDelay:(NSTimeInterval)delay
{
	[self performSelector:@selector(fadeOut) withObject:nil afterDelay:delay];
}

- (void)fadeOut
{	
	[UIView beginAnimations:nil	context:NULL];
	[UIView setAnimationDuration:.5];
	[self setAlpha:0];
	[UIView commitAnimations];
	
	[self performSelector:@selector(removeFromSuperview) withObject:nil afterDelay:1.5];	
}

// End of code... if there's a better way to format this for the mailing list, I'm all ears :)

In my AppDelegate, I call the splash screen's "fadeOutAfterDelay:" method *before* adding it to the window. Next I add the tab bar controller's view to the window. Finally, I add the splash screen view to the window.

This way, you split the work - the tab bar controller gets pulled from the nib, and the splash screen gets created quickly and lets you control how "default.png" is going to appear to disappear.

The simpler explanation is:

1. Keep the tab bar controller in MainWindow.xib

2. Create your splash screen manually (from a UIView subclass)

That might be why I haven't had any problems like those you described.

---- Thing Two: loadView vs. viewDidLoad ----

You'd only override your controller's "loadView" (MT: "LoadView" (surprise!)) when creating your UI entirely by hand. 

If you're using a nib for your controller's UI and you want to do fiddle with its views *after* the nib-loading bits have done their work, you'd use "viewDidLoad".

If you load from a nib and try to override "loadView", you're basically getting in the way of the nib-loading bits, and stuff is gonna break (by the by - as a side note for anybody else reading this - at the end of "loadView/LoadView", be sure to assign the top-level view you've created to the controller's view property - if you don't, your hard work in "loadView" will).

If, on the other hand, you *did* load from a nib, then you'd use "viewDidLoad" for any manual fiddling. You can also use it even if you've overridden "loadView".

By the time "viewDidLoad" is called, "loadView" has already run (whether the base class's implementation or your override), so anything that was in your nib, or anything you created in "loadView", will be available for meddling :)

Apologies if you already know most of this - wanted to clarify just in case, and also for the benefit of anybody reading this who isn't familiar with the order in which the various view loading/appearing/etc. methods are called - it can be a bit confusing.

---- Thing Three: When it's a matter of preference to go with "loadView" or "viewDidLoad" ----

The reality is that you don't *have* to override "loadView" even if you're doing your UI by hand.

It's a reasonable assumption that your view controller is going to need a top-level view.

If you override "loadView", you have to create that top-level view yourself.

If you override "viewDidLoad", and if you call [super viewDidLoad] (MT: "base.ViewDidLoad()"), then your top-level view will already have been created - you just need to add stuff to it now.

I use "loadView" because it makes it clear that I'm building the UI by hand - someone looking at the code won't have to guess. That's the main reason for it - for me. Just a style/preference.

However, these aren't always equal. Take UITableViewController subclasses for instance - unlike UIViewController where the "view" property will, by default, be a plain old UIView, a UITableViewController's "view" property will return a UITableView (you can get around that, but it seems kinda... I don't know... unnecessary and crazy? I've done it, but only with good reason).

In the case of a UITableViewController subclass, you'd probably be better off just using "viewDidLoad" to play it safe. You can still use "loadView" as long as you call "[super loadView]" / "base.LoadView()", but when there's something "special" being done for me behind the scenes, unless I need to work around a bug in the base class itself, I go with "viewDidLoad".

---- No more things ----

I hope that helps. Or at least makes sense. Or furthers the discussion.

Ed - I'm guessing you already know most of this, so I apologize if I'm coming off as well-let-ME-tell-YOU-something... it's that this is a discussion I wish, wish, *wish* someone had sat me down and had when I was getting started with iPhone development, and I assume most MT devs are new to iPhone development (having picked it up *because* of MT), and so aren't familiar with all this other bidniss.

Also, I think I agree on "viewWillAppear" - for the most part (I use it sparingly, and usually just for UI transition-y stuff, but your point makes sense, and it's something I need to think about/futz with - but I have a problem with brevity, so I'm going to shut myself up before I bring down the internet with all my babbling).

- Rory Blyth

On Nov 27, 2009, at 11:06 PM, Ed Anuff wrote:

> Actually, the problem with the tab bar controller occurs when you don't have your (populated) tab bar controller in your main nib.  For example, if you had a splash screen/login screen that you were trying to get up as quickly as possible and then loaded the tab bar controller separately.  In my app, that's what I'm trying to make happen as quickly as possible.
> 
> However, your second point in interesting, if you're doing stuff in loadView (or viewDidLoad) then I think your code is still being executed during nib load time.  That's why I was suggesting if you want to really get the maximum benefit of not using a monolithic nib, you'd look at overriding viewWillAppear, which should be getting called when you actually cause a specific tab view to be displayed, so you could defer constructing the UI for that tab till that point.   As I said before, the idea isn't that you're going to get your speed improvement just by doing the same thing in your code that  initWithNibName:bundle: does, it's going to come from breaking up the work so that it doesn't happen all at once when you load the main nib.
> 
> On Fri, Nov 27, 2009 at 5:55 PM, Rory Blyth <lists at rory.me> wrote:
> Regarding the use of tab bar controllers as main controllers and associated issues... I've never experienced that. The only problem I ever had was the one following a new release of Apple's dev tools - they fiddled with something, and my stuff broke. It was working for months before that. And it was far more convenient than doing it by hand (even though doing it by hand is trivial).
> 
> I'm not even sure how I'd use a tab bar controller *without* it being my main controller. It might be habit, but... I don't see how else you'd do it if you're writing a tab bar based app.
> 
> When it comes to coding by hand, I override loadView (or "LoadView" in MT) and just do it to it. There are many reasons for doing this - the most important, for me, have been:
> 

-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.ximian.com/pipermail/monotouch/attachments/20091128/3575116b/attachment.html 


More information about the MonoTouch mailing list