Ever since I've taken one of my UIViewController subclasses and present it to the user in the form of a modal view, with presentModalViewController:animated.. I haven't been able to dismiss it using:
[self dismissModalViewControllerAnimated:YES];
I do believe this is some odd mixup with how I'm instantiating a UINavigationController on the modalViewController, with code that looks like the following (similar code is also in the App Delegate):
UIWindow *window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
navigationController =
[[UINavigationController alloc] initWithRootViewController:self];
navigationController.navigationBar.barStyle = UIBarStyleBlackOpaque;
[window addSubview:navigationController.view];
[window makeKeyAndVisible];
The navigation stack works as expected, but unless I remove it, I can't dismiss the modal view controller any longer. I'm under the impression that I shouldn't be adding a subView to UIWindow more than once and that's just one of the problems.
Make sure you call dismissModalViewControllerAnimated on the parent of the modal view controller, not on the modal view controller itself.
Related
I have an UITabBarController added programmatically one ViewController as ModalViewController.
It acts perfectly normal, when touching/switchen between the tabs.
But when touching the selected tab again, the delegate didselectviewcontroller did not get called.
Strange behaviour, if I setup a new project with TabBarController template, this is acting normal and everytime I touch a tab its delegate is being called.
What I noticed, if I touch 2-5px above the tabbar, the tab is being touched and the delegate is being called.
There is no view above the tabbar, I checked this 100 times.
if I touch in the lightgray area, the touch on the tab is being fired.. thats a miracle to me, anybody any idea?
Code:
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.tabBarController = [[UITabBarController alloc] init];
_splashScreen = [[SplashScreenController alloc] initWithNibName:#"SplashScreenView" bundle:nil];
self.window.rootViewController = _splashScreen;
[self.window makeKeyAndVisible];
if finished loading data in background,
//getting UIViewControllers from Config and adding to NSMutableArray *tbcArr;
[self.tabBarController setViewControllers:tbcArr];
self.tabBarController.customizableViewControllers = nil;
self.tabBarController.delegate = self;
[self.splashScreen presentModalViewController:self.tabBarController animated:YES];
My project is a view based project to start off.
So app delegate launches as per normal.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];
return YES;
}
Then my first viewcontroller is called and it shows two UITextFields so the user can enter their credentials and log on.
When that succeeds I call another view controller in which I add a UINavigationController and a UITabBarController to the view. As can be seen below.
- (void)viewDidLoad
{
[super viewDidLoad];
UINavigationController *localNavigationController;
tabBarController = [[UITabBarController alloc] init];
NSMutableArray *localControllersArray = [[NSMutableArray alloc] initWithCapacity:2];
Hello *firstViewController;
firstViewController = [[Hello alloc] init];
localNavigationController = [[UINavigationController alloc] initWithRootViewController:firstViewController];
[localNavigationController.tabBarItem initWithTitle:#"Test" image:[UIImage imageNamed:#"tabBarIcon.png"] tag:1];
//[localNavigationController.tabBarItem initWithTabBarSystemItem:UITabBarSystemItemDownloads tag:1];
firstViewController.navigationItem.title=#"New Requests";
[localControllersArray addObject:localNavigationController];
[localNavigationController release];
[firstViewController release];
Test *secondViewController;
secondViewController = [[Test alloc] init];
localNavigationController = [[UINavigationController alloc] initWithRootViewController:secondViewController];
[localNavigationController.tabBarItem initWithTitle:#"Test" image:[UIImage imageNamed:#"tabBarIcon.png"] tag:2];
secondViewController.navigationItem.title=#"Existing";
[localControllersArray addObject:localNavigationController];
[localNavigationController release];
[secondViewController release];
// load up our tab bar controller with the view controllers
tabBarController.viewControllers = localControllersArray;
// release the array because the tab bar controller now has it
[localControllersArray release];
// add the tabBarController as a subview in the window
[self.view addSubview:tabBarController.view];
}
This seems to work ok, so far. There was a problem off both the Navbar and Tabbar being dropped to low by the height of the status bar, but that was corrected once i hid the status bar.
Is there any reason I should not do things this way? Is it bad practice or will i run into some problems with it down the road?
I could set up both the Navbar and the Tabbar from the app delegate and just hide them both during the log-on screen. That's the only other option I see.
I appreciate any feedback that you guys can offer. I feel nervous about the results of what I have done so far, expect it might blow up in my face.
Many Thanks,
-Code
You should generally not directly add the views of UINavigationController and UITabBarController as subviews of your own view controllers. This kind of 'view controller containment' is tricky to get right unless you use the new iOS 5 APIs.
The reason is that the actual view controllers will not receive certain important messages like viewDidAppear: and rotation messages. You will notice strange rotation bugs and other weird issues cropping up. You can forward these methods yourself from the parent view controller and things will work OK, but in your case you have no need to do this because you're just trying to show a standard tab bar controller.
Generally you should have one view controller set up as the UIWindow's root view controller. This is normally a UINavigationController, UITabBarController, etc. The parent UIWindow will send rotation events and other messages to this controller. The standard 'container' controllers like UITabBarController will then forward these messages to their children so everything works correctly.
If I were you, I would always have the tab bar controller as the window's root view controller. When your app starts (i.e. in application:didFinishLaunchingWithOptions:), create an empty tab bar controller and set it up as the root view controller:
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// create a basic empty tab bar controller
self.tabBarController = [[[UITabBarController alloc] init] autorelease];
// ...
// Set up the window's root view controller
self.window.rootViewController = tabBarController;
// ...
}
Now, whenever you present stuff on the screen, it should be a child of the root view controller, in your case the tab bar controller.
So once the root view controller is set up, you can check to see if the user is already logged in. If they are, you can then set up your navigation controller and the tab items, and add them to the tab bar controller.
If the user is not logged in, you can show your login view controller over the top of the tab bar controller using presentModalViewController:animated::
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// ...
// Set up the window's root view controller
self.window.rootViewController = tabBarController;
if (isUserLoggedIn()) {
[self setupTabsAndStuff];
} else {
LoginViewController *loginVC = [[[LoginViewController alloc] init] autorelease];
[self.tabBarController presentModalViewController:loginVC animated:NO];
}
// ...
}
The animated:NO will cause the login screen to be immediately visible after starting the app with no animation.
Once the user enters correct details you can again call your setupTabsAndStuff method and dismiss the login view controller again.
So to summarize:
Rather than having your own UIViewController into which you place the tab bar controller's view, just use the tab bar controller directly.
Install the tab bar controller as the window's root view controller. The tab bar controller will then correctly forward rotation events and other special messages to the view controllers it contains.
Always present views and view controllers as children of the root view controller. If you need to show something full-screen use presentModelViewController:animated
Try to set the frame of *tabBarController.
[tabBarController.view setFrame:self.view.bounds];
// add the tabBarController as a subview in the window
[self.view addSubview:tabBarController.view];
I launch a modal view controller and in its init I have the following code:
window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
viewController = [[UIViewController alloc]init];
[window addSubview:viewController.view];
[window makeKeyAndVisible];
I need the viewController for entering some data in it. How can I release the window and viewController so that I can go back to the viewController that launched the modal view controller? At this point, after I'm done doing what I need in the modal view, I am trying:
[viewController.view removeFromSuperview];
[window removeFromSuperview];
but just end up with the modal view's parent (the one that lanched the modal view) just frozen(it's not really frozen, it is just not user interactible because there are the window and view controller in front of it and the view controller doesn't have size and background
iOS app should only contain one UIWindow.
And you can easily present UIViewController with in viewController
[self presentModalViewController:navigationController animated:animated];
Then just call in that viewController to dismiss it:
[self dismissModalViewControllerAnimated:YES];
I have created a view based application and in the appdelegate .h file I have created UINavigationcontroller object and added it to window subview. Next in the app did finish launching I had allocated and initialised a class that I want to be viewed when the app first launches with the navigation bar on the top of it.
So this is the code I have did to add the class to navigation bar
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
// Add the view controller's view to the window and display.
[window addSubview:navController.view];
//test is the class that i want to be viewed first when the app launches first
test *t = [[test alloc]initWithNibName:#"test" bundle:nil];
[navController.view addSubview:t.view];
[window makeKeyAndVisible];
return YES;
}
but the problem is my test class launches but it does not display the navigation bar. What may be the problem?
Use some like this (add these lines)
appDelegate.h
UINavigationController *navController
#property (nonatomic,retain) UINavigationController *navController;
and in .m
test *t = [[test alloc]initWithNibName:#"test" bundle:nil];
self.navController=[[[UINavigationController alloc] initWithRootViewController:t] autorelease];
[window addSubview:self.navController.view];
[window makeKeyAndVisible];
it helps you.
From other view you need to make object of the appDelegate class then access the navigation controller of that class
see this
yourAppDelegateClass *objAppDelegate=(yourAppDelegateClass *)[[UIApplication sharedApplication] delegate];
now for pushing a view
SecondView *s=[[[SecondView alloc] initWithNibName:#"yournibName" bundle:nil] autorelease];
[objAppDelegate.navController pushViewController:s animated:YES];
this concept help you.
You shouldn't add your own view controller's view as a subview of the navigation controller. This way, you hide the navigation controller's view (including the navigation bar) and it makes the navigation controller pointless, because it doesn't know that your view controller wants to be part of its navigation stack.
Instead, push it on the navigation controller's stack by using:
[navController pushViewController:myViewController animated:NO];
Navigation bar is not coming because you are setting navigation controller's view to your test view. You can do it by setting navigation controller's view controllers which requires an array as -
test *t = [[test alloc]initWithNibName:#"test" bundle:nil];
NSArray *viewControllers = [[NSArray alloc] initWithObjects:t,nil];
[self.navigationController setViewControllers:viewControllers];
First, I don't see where you alloc init your navController. Secondly, you don't add a viewController to a navigation controllers view, rather you push the view controller onto the stack, like this:
[navController pushViewController:t animated:NO];
Hope this helps.
my first xib contains a ScrollView with a springboard like interface in MainWindow.xib:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
navController = [[UINavigationController alloc] init];
[navController setNavigationBarHidden:YES];
[window addSubview:navController.view];
[window sendSubviewToBack:navController.view]; }
When a button is clicked the FirstViewController appears with a tableview and a navigation controller:
- (void) buttonPushed:(id)sender {
FirstViewController *firstViewController = [[FirstViewController alloc] init];
[navController pushViewController:firstViewController animated:YES];
[firstViewController release];
[window addSubview:navController.view]; }
When I click the back button in Navigation Controller to go back to springboard, I get the springboard xib, but unresponsive to touches with a Navigation Bar on top!
- (void)goHome:(id) sender {
[self.view removeFromSuperview];
How can I go back to springboard screen (mainwindow.xib) without having the navigation bar stacked on top, and be responsive to touches ?
Why don't you set the springboard view to be the root view controller of your navigation controller and get rid of any UI in the window?
I think that the window shouldn't have any UI elements accept view of view controllers that are added to it (by navigation controller or by tab bar controller).
This way you won't have to reinvent the wheel for the first view to load from the springboard view and the back button will work properly.
You can set the navigationBarHidden property to false in the viewDidLoad method of the root view controller (the view controller of the springboard view).
Did you try calling [navController setNavigationBarHidden:YES]; in your mainview viewWillAppear callback ?
- (void)applicationDidFinishLaunching:(UIApplication *)application
{
UIViewController *rootController = [[MyRootViewController alloc] init];
navigationController = [[UINavigationController alloc]
initWithRootViewController:rootController];
[rootController release];
window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
[window addSubview:navigationController.view];
[window makeKeyAndVisible];
}