views:

41

answers:

3

I have a Tab Bar Application and I want to simply display a view (splash screen) once the didFinishLaunchingWithOptions method loads up the tab bar controller. Why is this so hard? Please show me how I'd load up and show a Xib file called SplashView.xib below and display it:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {    

    // Override point for customization after application launch.

    // Add the tab bar controller's view to the window and display.
    [window addSubview:tabBarController.view];
    [window makeKeyAndVisible];

    // Load up and show splash screen Xib here

    return YES;
}
+1  A: 

I would do something like:

    UIImageView *imageView = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"Splash.png"]];
    [imageView setCenter:CGPointMake(240, 160)];

    self.view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 480)];
    [self.view addSubview:imageView];

    [imageView retain];

    [NSTimer scheduledTimerWithTimeInterval:3.0 target:self //display for 3 secs
     selector:@selector(continueLoadingWhatever:)
     userInfo:nil
     repeats:NO];

And then...

- (void)continueLoadingWhatever:(id)sender {
    //do whatever comes after here
}

I probably wouldn't do this in the app delegate, but in the root view controller. You should never have to add anything unnecessary directly to the window, especially if it contains interaction (I know this doesn't).

diatrevolo
I actually have some code that does connectivity testing in my SplashViewController so I cannot just use an image. This is good code though so thanks.
Nissan Fan
+1  A: 

In your app delegate's header file:

@interface AppDelegate {
    ...
    IBOutlet UIView *splash; // add this line
}

In IB open the SplashView.xib, set the File Owner's class to the class of your app delegate, connect the splash outlet. Add this to show a splash view:

[[NSBundle mainBundle] loadNibNamed: @"SplashView" owner: self options: nil];
[window addSubview: splash];

You will possibly want to hide the splash view too:

[splash removeFromSuperview];
[splash release];
splash = nil;

You could use UIView animation block to fade out the splash view to be extra-cool. That said, splash screens are evil.

I think the app delegate is indeed a better place for this kind of stuff.

Costique
Why do you feel the app delegate is a better place for this? Just curious, maybe I'm missing something important.
diatrevolo
Because the splash screen is a one-shot UI element which is displayed while most of your code is not even initialized. Basically, only the main nib is loaded, and at the bare minimum the only object in your code is guaranteed to be fully inited, the application delegate. For exactly the same reason why it receives applicationDidLaunch-kind of notifications, IMHO, it is the ideal candidate to manage the splash screen. That said, encapsulating the functionality in a special class is not a bad thing either, if you find it more comfortable, but is far from being necessary.
Costique
Yes, I can see what you mean. Very specific cases.
diatrevolo
+3  A: 

First thing I'd mention is that splash screens are specifically frowned upon in the HIG. Especially ones that only serve to make the user wait & stare at some logo they don't care about.

Now that rant is out of the way, I'll assume that you probably have some loading going on that you'd like to have happen before the tabs are displayed.

In this case I don't load up a tab bar in the MainWindow.xib. Instead I launch my single view (with XIB) that does the loading. The reason is this: You'll pay for the loading of all of those views before you can even see your splash screen.

In the case of loading data, sometimes these tabs depend on this data being loaded, so it makes more sense to wait to load up the tab bar controller.

The app delegate ends up looking like this:

- (void)applicationDidFinishLaunching:(UIApplication *)application {
    [window makeKeyAndVisible];
    [window addSubview:splashController.view];  //this assumes MainWindow.xib defines your splash screen, otherwise....

    UIViewController *splashController = [[SplashController alloc] initWithNibName:@"SplashController" bundle:nil];
    splashController.delegate = self;
    [window addSubview:splashController.view];

    //hang on to it in an ivar, remember to release in the dealloc
}

Then in the splash screen controller, when I'm done loading, I do this:

-(void)doneLoading {
    [self.delegate performSelector:@selector(splashScreenDidFinishLoading)];
}

Of course self.delegate doesn't exist, and it can be added simply like this:

//header
@property (nonatomic, assign) id delegate;

//implementation
@synthesize delegate;

Then just make sure and implement that method on the app delegate:

-(void)splashScreenDidFinishLoading {
     //load up tab bar from nib & display on window
     //dispose of splash screen controller
}

I've used this pattern in a handful of apps and is simple and works well. You could also choose to do a nice transition animation in the method above.

Hope this helps.

Ben Scheirman
I find this approach very interesting, but I must be doing something terrible because the result of me using this code are two compiler messages: 1. Error: Request for member 'delegate' in something not a structure or union 2. Warning: 'UIWindow' may not respond to '-addSubView:'
Nissan Fan
I typed this without compiling :) It's addSubview (lowercase 'v').
Ben Scheirman
Also the delegate is something that you'd implement yourself as a property on the splash view controller. @property (nonatomic, assign) id delegate; I've updated the question to show this...
Ben Scheirman