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;
}
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.
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.
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).
Related
I am currently trying to add a Activity Indicator View to my Splashscreen. It only should appear once - at the first start of the App. Some images are created that are needed for the App, but it takes some time.
While the images are created, the Splashscreen is still visible. So I thought it could maybe be possible to add a Activity Indicator as a Subview of the Splashscreen or at least add it somehow over the Splashscreen.
Is there a possibility to make this possible?
Thanks for your help in advance.
You will need to add the "Splashscreen" as top most view to your window, the "splashcreen" it self is not a view. The system wil just display the default.png:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
splashScreen = [[UIImageView alloc] initWithFrame:CGRectMake(0.0f, -20.0f, 320.0f, 480.0f)];
splashScreen.backgroundColor = [UIColor blackColor];
splashScreen.image = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"Default" ofType:#"png"]];
[self.window addSubview:splashScreen];
[self.window makeKeyAndVisible];
[self performSelector:#selector(applicationDidStart) withObject:nil afterDelay:0.1];
return YES;
}
splashScreen is a class variable, you could add an Activity Indicator to the splashView.
Then in the applicationDidStart did start methods place the code that will take some time:
- (void) applicationDidStart {
// some thing that takes a while
[splashScreen removeFromSuperView];
[splashScreen release], splashScreen = nil;
}
I dont think it is possible to add a subview to the Splash Screen.
There is a workaround where in you can push an intermediate view on applicationDidFinishLaunching and have the background image of the view as same as that of the splash screen.
Now you can do the stuff where your images are created on this view.
Once images are created, Call a method in applicationDelegate which will pop the intermediate view and add your regular view.
Hope this helps you.
I'm new to iphone app development and I try to build an app with a splash screen. I need the splash screen, because my app needs to load a xml file from the web. during this, the app should show a splash screen (logo + message "loading xml...") and after successful loading it should proceed to the main screen.
I started the app with a tabbar template, because this should be shown after the splash screen. the first tab has a navigation controller (configured in IB) which loads a custom view controller (I called it searchViewController.h/.m/.xib) on his stack.
my problem:
if I run my app, the screen keeps for a long time black and then there is very shortly the splash-screen shown (as it moves down/disappearing) and then the tabbar with the other attached views are shown. but while the screen is black, I can see in the console my control NSLog-messages from the XML parsing.
this means: the app starts, fetching XML, parsing XML, showing splash screen for about 1ms and then show the main app. but I want it to show the splash screen already during the XML fetching and parsing.
my AppDelegate.m:
#import "AppDelegate_iPhone.h"
#import "startPage.h"
#implementation AppDelegate_iPhone
#synthesize window;
#synthesize tabBarController;
#synthesize theVendors;
#pragma mark -#pragma mark Application lifecycle
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:
(NSDictionary *)launchOptions {
[window addSubview:tabBarController.view];
//initialise the splash screen from class startPage as loadingPage
startPage *loadingPage = [[startPage alloc] init];
//present splash screen as modalview of the tabbar controller
[self.tabBarController presentModalViewController:loadingPage animated:NO];
//make the hole thing visible (should be visible, but isn't (that's the problem))
[self.window makeKeyAndVisible];
//call the splash-screen controller for doing the XML work
[loadingPage initVendors];
[loadingPage release];
return YES;
}
and now the spash-screen method for the XML stuff:
-(void)initVendors{
vendors *theVendorsLocal = [[vendors alloc] initWithDictionary];
NSURL *xmlURL = [NSURL URLWithString:#"http://.../muuhh.xml"];
fParser = [[NSXMLParser alloc] initWithContentsOfURL:xmlURL];
[fParser setDelegate:theVendorsLocal];
[fParser setShouldProcessNamespaces:NO];
[fParser setShouldReportNamespacePrefixes:NO];
[fParser setShouldResolveExternalEntities:NO];
[fParser parse];
[xmlURL release];
if(UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad){
//AppDelegate_iPad *del = (AppDelegate_iPad *)[UIApplication sharedApplication].delegate;
//del.theVendors = theVendorsLocal;
}else{
AppDelegate_iPhone *del = (AppDelegate_iPhone *)[UIApplication sharedApplication].delegate;
del.theVendors = theVendorsLocal;
}
//detach the modalview from the tabbar controller
[self.parentViewController dismissModalViewControllerAnimated:YES];
}
so the problem is, that the whole parsing stuff is done BEFORE the splash screnn is shown.
why??
as you can see, I load the tabbarcontroller.view, put it as subview of window, set the modalview and do the [self.window makeKeyAndVisible] before I call the [loadingPage initVendors]; method...
why is the splash screen shown after the whole parsing stuff (actually it's not even shown, I just see how it slides out)??
thanks for any help...
I tried to move the [self.window makeKeyAndVisible] to the very top, but the results are still the same:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
breakpoint1 -> [self.window makeKeyAndVisible];
breakpoint2 -> [window addSubview:tabBarController.view];
//initialise the splash screen from class startPage as loadingPage
breakpoint3 -> startPage *loadingPage = [[startPage alloc] init];
//present splash screen as modalview of the tabbar controller
[self.tabBarController presentModalViewController:loadingPage animated:NO];
...
I also tried around with other arrangements, nothing worked. finally I started setting breakpoints and using NSLog to show me, how the app is calling every method. while observing the screen, I found out, that the method-call of [self.window makeKeyAndVisible] doesn't show the view directly after the call..
I marked the breakpoints in the source code above: I would have assumed, that after breakpoint1 the black screen would disappear and a white screen is shown (the window) and afterwards at breakpoint2 the tabbar is shown (at least at breakpoint3). but nothing happens. the screen remains black until the whole idFinishLaunchingWithOptions-method is finished.
I also placed some breakpoints in the applicationDidBecomeActive-method, which is called after the didFinishLaunchingWithOptions-method. if the debugger breaks in one breakpoint in the applicationDidBecomeActive-method, even then there is nothing shown on the screen, the screnn still remains black.
finally after leaving the applicationDidBecomeActive-method the tabbar is shown.
I have no clou, where the app "goes" after leaving the applicationDidBecomeActive-method.
And I would like to know, where and when the tabbar is actually shown?
why does [self.window makeKeyAndVisible] doesn't show the window if I stop at a b reakpoint directly behind this call?
I suspect the tabbarcontroller is unable to present your splashscreen modally in time, because at that point your window is still invisible, hence hiding the view hierarchy attached to it. The splashscreen belongs to this hierarchy too.
Try moving the "makeKeyAndVisible" method to the beginning of your didFinishLaunchingMethod, otherwise making the view hierarchy is visible until after parsing your xml is complete - just as you are observing it.
I'm having a problem making a universal app... In the application delegate I set up the main navigation for ipad and iphone:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
window = [[UIWindow alloc] initWithFrame:[ [UIScreen mainScreen] bounds]];
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
// The device is an iPad running iPhone 3.2 or later.
[self putSplitView];
} else {
[self putTabBar];
}
[window makeKeyAndVisible];
return YES;
}
- (void)putSplitView {
RootiPadViewController *rootiPadViewController = [[RootiPadViewController alloc] init];
UISplitViewController *splitController = [[UISplitViewController alloc] init];
splitController.viewControllers = [NSArray
arrayWithObjects:rootiPadViewController.seccionesView,
rootiPadViewController.noticiasView,
nil];
[window addSubview:splitController.view];
}
- (void)putTabBar {
TabBarController *tabBar = [[TabBarController alloc] init];
[window addSubview:tabBar.view];
}
RootiPadViewController is in charge of loading data an generating the panes for the splitView, so that its initialization incorporates a modalView with a loader, such that:
#implementation RootiPadViewController
#synthesize seccionesView, noticiasView;
- (id)init {
if ((self = [super init])) {
SeccionesVC_iPad *sec = [[SeccionesVC_iPad alloc] init];
NoticiasVC_iPad *not = [[NoticiasVC_iPad alloc] init];
self.noticiasView = not;
self.seccionesView = sec;
Init *initVC = [[Init alloc] init];
[self presentModalViewController:initVC animated:YES];
}
return self;
}
The code compiles without warnings, but for some reason the loadView method of initVC is never called and the modal view doesn't appear...
Any ideas why this might be happening?
thanks for your help!
Antonio
I know this is last year's question, but while I think I found the cause, I'm still trying to figure out a good non-hack solution for dealing with this.
Assuming you're working for iPad:
Upon initialization, splitViewController Master's viewDidLoad is called first, then the Detail's viewDidLoad.
If you attempt to put a modal window initializer in the Master's viewDidLoad, nothing seems to happen: This is because the Detail's viewDidLoad has executed after your modal view's viewDidLoad, resulting in your modal view being hidden away.
Breakpointing the modal view class, I see it enter initWithNibName in the modal view. I don't use ARC and here I assume your modal view doesn't dealloc until the program quits - The modal view is still there, but there's no way to get to it.
In iPhone:
Disregard this - because there is NO splitViewController for the iPhone. In fact, the default Universal splitViewController project treats the Master and Detail view as separate pages. So at application init, all you have to worry about is the Master viewDidLoad.
This is why, in a Universal project, pushing a modal view in viewDidLoad works as intended in the iPhone emulator, but NEVER in the iPad emulator.
Note that this only applies during the viewDidLoad stage; if you push a modal view AFTER viewDidLoad into the Master window, the modal view works as intended.
Maybe you should present your modal ViewController from the RootViewController 'ViewDidLoad' method, instead of the 'init' method... Not sure the view hierarchy is created that soon
How can I accomplish the following:
When my app loads a UIView will show 4 buttons
Clicking on a button will load a UITabBarController (not a UIView with a UITabBar) that can display multiple views.
This seems challenging to me, because in order for me to use the UITabBarController I need to add this to the window's subview in my appDelegate. By doing so, my app automatically will load with the UITabbarController in the root view.
You don't need to add the UITabBarController in the application delegate, that's just the most common way to use it. You can have your initial view use a simple UIViewController, then when the button is pressed load the UITabBarController (either programmatically or from a nib), and then display it.
The following is an example of what might be in your app delegate:
- (BOOL) application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// viewController is a UIViewController loaded from MainWindow.xib with a button that calls loadTabBarController
[window addSubview:viewController.view];
[window makeKeyAndVisible];
return YES;
}
- (IBAction) loadTabBarController {
self.tabBarController = [[[UITabBarController alloc] initWithNibName:#"MyTabBarController" bundle:nil] autorelease];
[viewController.view removeFromSuperview];
[window addSubview:tabBarController.view];
}
I am very new to Obj-C and learning iphone development.
My question is how to add subview from app delegate.
Lets say I added subview called "MainView" from "applicationDidFinishLaunching" method.
- (void)applicationDidFinishLaunching:(UIApplication *)application {
MainViewController *aViewController = [[MainViewController alloc] initWithNibName:#"MainView" bundle:nil];
self.mainViewController = aViewController;
[aViewController release];
[window addSubview:mainViewController.view];
// Override point for customization after application launch
[window makeKeyAndVisible];
}
"MainView.xib" file has a button to show its child view. When the button is clicked, it calls "showChildView" method.
- (IBAction)showChildView:(id)sender {
if (self.childViewController == nil) {
ChildViewController *childController = [[ChildViewController alloc] initWithNibName:#"ChildView" bundle:nil];
self.childViewController = childController;
[childController release];
}
[self.view insertSubview:childViewController.view atIndex:0];
}
From this code, when app launches, it shows "MainView" with a button. But when I clicked the button, the button is still visible as well as the content from the "ChildView.xib" file too.
How can I hide the "MainView" when I pressed the button and show only the contents of the "ChildView"?
Thanks for your help in advance.
well, you have to remove the original view first, before inserting the new subview, do it this way
- (IBAction)showChildView:(id)sender {
if (self.childViewController == nil) {
ChildViewController *childController = [[ChildViewController alloc] initWithNibName:#"ChildView" bundle:nil];
self.childViewController = childController;
[childController release];
}
[self.mainViewControlle.view removeFromSuperView];
[self.view insertSubview:childViewController.view atIndex:0];
}
Hope this helps.
You might want to check out the Utility App sample -- it demonstrates switching between two views with animation and adding/removing views from parent views.
you might want to create a navigation controller in the main view and than push the childviewcontroller onto it when invoking showChildView. You'll get the back navigation button for free that way