iphone dev: splash screen with tabbar controller + modalviewcontroller - iphone

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.

Related

How to load all tab view controllers on application start?

Basically when i start my application I show tab view controller, and it shows first tab, and loads only first tab. I would like at the same time to preload rest of the view controllers. I have found this post - Load All TabBar Views
But I am getting error. I call [subcontroller view] in viewDidLoad method of tab bar controller. I am using storyboard. The problem is that I am getting error - Could not load NIB in bundle: 'NSBundle'
What am i missing?
EDIT:
I will try to be more concise - Tabbar has 4 view controllers, corresponding to 4 different tabs. When user presses tab, corresponding view controller is loaded. When you first launch the app, only the first view controller is loaded. Other view controllers are not loaded, they are loaded after user taps their tabs. I want all those controllers to load to memory so i can do something with them (they are not shown on screen).
// load all view controller in appdelegate.h file in project
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
ViewController1 *view1=[[ViewController1 alloc]initWithNibName:nil bundle:nil ];
view1.tabBarItem.image = [UIImage imageNamed:#"1.jpg"];
UINavigationController *navi1=[[UINavigationController alloc]initWithRootViewController:view1];
ViewController2 *view2=[[ViewController2 alloc]initWithNibName:nil bundle:nil ];
view1.tabBarItem.image = [UIImage imageNamed:#"1.jpg"];
UINavigationController *navi2=[[UINavigationController alloc]initWithRootViewController:view2];
NSArray *navi= [[NSArray alloc]initWithObjects:navi1,navi2,navi3,navi4,nil];
UITabBarController * bar=[[UITabBarController alloc]init];
NSArray *navi= [[NSArray alloc]initWithObjects:view1,view2,nil];
bar.viewControllers=navi;
[self.bar setViewControllers:navi animated:YES];
self.window.rootViewController=self.bar;
[self.window addSubview:bar.view];
[self.window makeKeyAndVisible];
return YES;
}
}
Like what i understand u wanna to load all other view and set it ready to lunch it when u press on tap :)
if that what u want u can load it and build it all in background and add it to view but u need to hide it until u press u show the view u want and hide the other views.
this answer work if u build your custom tab :)

switching to parent view controller on changing the tab from one tab to another

In my application after logging in I am presenting a tab bar with four tabs in it and my question if how to come back to the parent view of first tab every time when my app comes from background to foreground
YourAppDelegate *appdelegate = ((YourAppDelegate*)[[UIApplication sharedApplication]delegate]);
appdelegate.tabBarController.selectedViewController = [appdelegate.tabBarController.viewControllers objectAtIndex:0];
in appdelegate.m file
I'm adding splash view. You should call tabbar object and load it in your view.
- (void)applicationWillResignActive:(UIApplication *)application {
viewController1=[[splasView alloc]initWithNibName:#"splasView" bundle:nil];
[self.window addSubview:viewController1.view];
[self.window makeKeyAndVisible];
}
let me know if any query you have

Show a view as a splash screen in didFinishLaunchingWithOptions

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).

UIVIewController wrong position before rotate

I have the starter point of my application this window (white background):
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
mainScreen = [[MainScreenController alloc] initWithNibName:#"MainScreenController" bundle:[NSBundle mainBundle]];
// add it to the main window
[window addSubview:[mainScreen view]];
// show view
[window makeKeyAndVisible];
NSLog(#"started");
return YES;
}
And MainScreenController is an UIViewController with (blackBackground) created by the Interface Builder.
The application executes perfectly but the first time, the black view is moved up like the height of the status iphone pannel (leaving white rectangle at the bottom).
If I rotate the iPhone twice, the view is perfectly positioned in the right place, filling all the screen with the black background.
Any idea?
Thanks!
Check your MainScreenController settings in IB. You should select a simulated status bar element (cmd-1) so that the view is positioned below the status bar initially.
EDIT - Why are you calling the initWithNibName in your delegate here? Most of the app templates I've used had the main view nib initialized by the OS and you use IB and set an IBOutlet in the main VC interface. You probably have an initial frame origin of {0,0} after calling initWithNibName. Add these lines after your initWithNibName call to verify this:
NSLog(#"mainscreen frame=%#", NSStringFromCGRect(mainscreen.view.frame));
CGRect frame = mainscreen.view.frame;
frame.origin.y = 20; // move below status bar
mainscreen.view.frame = frame;

Gap appears between navigation bar and view after rotating & tab switching

My iphone application is showing strange behavior when rotating: a gap appears between the navigation title and content view inside a tab bar view (details on how to reproduce are below). I've created a tiny test case that exhibits the same problem: a custom root UIViewController, which creates and displays a UITabBarController programmatically, which has two tabs: 1) plain UIViewController, and 2) UINavigationController created programmatically with a single plain UIViewController content view.
The complete code for the application is in the root controller's viewDidLoad (every "*VC" class is a totally vanilla UIViewController subclass with XIB for user interface from XCode, with only the view background color changed to clearly identify each view, nothing else).
Here's the viewDidLoad code, and the shouldAutorotateToInterfaceOrientation code, this code is the entire application basically:
- (void)viewDidLoad {
[super viewDidLoad];
FirstVC *fvc = [[FirstVC alloc] initWithNibName:#"FirstVC" bundle:nil];
NavContentsVC *ncvc = [[NavContentsVC alloc] initWithNibName:#"NavContentsVC" bundle:nil];
UINavigationController *svc = [[UINavigationController alloc] initWithRootViewController:ncvc];
NSMutableArray *localControllersArray = [[NSMutableArray alloc] initWithCapacity:2];
[localControllersArray addObject:fvc];
[localControllersArray addObject:svc];
fvc.title = #"FirstVC-Title";
ncvc.title = #"NavContents-Title";
UITabBarController *tbc = [[UITabBarController alloc] init];
tbc.view.frame = CGRectMake(0, 0, 320, 460);
[tbc setViewControllers:localControllersArray];
[self.view addSubview:tbc.view];
[localControllersArray release];
[ncvc release];
[svc release];
[fvc release];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
Here's how to reproduce the problem:
1) start application
2) rotate device (happens in simulator, too) to landscape (UITabBar properly rotates)
3) click on tab 2
4) rotate device to portrait -- notice gap of root view controller's background color of about 10 pixels high beneath the Navigation title bar and the Navigation content view.
5) click tab 1
6) click tab 2
And the gap is gone! From my real application, I see that the gap remains during all VC push and pops while the NavigationController tab is active. Switching away to a different tab and back to the Nav tab clears up the gap.
What am I doing wrong? I'm running on SDK 3.1.3, this happens both on the simulator and on the device. Except for this particular sequence, everything seems to work fine. Help!
This problem occurs when you nest a UINavigationController within another UIViewController (in this case a UITabBarController). If you had the UINavigationController as the root view controller, then this problem wouldn't occur.
One solution may be to go in and alter the frame of the navigation bar (set the y origin from 0 to 20), but the documentation states explicitly not to do this. So to me, this is an indication that it isn't considered good UI to nest a UINavigationController - you shouldn't be doing it.
Please let me know what you think - thanks. :)
A workaround works in some occasion:
After rotating, force a refresh of the NavigationBar and therefore the frame of its view is resized properly. Some code like this:
(void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
// if _navigationController is showing
[_navigationController setNavigationBarHidden:YES];
[_navigationController setNavigationBarHidden:NO];
}