I'm building an app in which the root view/window is a tab-based view (created using the XCode wizard for creating a tab-based iPhone app), but there is also a point in the app where I want to create another tab-based view and present it modally.
I was having so much trouble creating the modal tab-based view in IB that I eventually just did it in code, kinda like this:
// *** In the event handler that causes the second-tab view to be presented ***
MyTabViewController *tabVC = [[MyTabViewController alloc] init];
[self presentModalViewController:tabVC.tabBarController animated:YES];
[tabVC release];
// *** Inside init() definition in MyTabViewController.m ***
UIViewController *vc1 = [[MyViewController1 alloc] init];
UIViewController *vc2 = [[MyViewController2 alloc] init];
tabBarController_ = [[UITabBarController alloc] initWithNibName:nil bundle:nil];
tabBarController_.viewControllers = [NSArray arrayWithObjects:vc1, vc2, nil];
tabBarController_.selectedIndex = 0;
This worked fine until I started trying to write to tabBarController_.tabBar.items to set the titles and images for the buttons, which it apparently doesn't want to let you do for a TabBar that is owned by a TabBarController, giving this error:
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason:
'Directly modifying a tab bar managed by a tab bar controller is not allowed.'
So I tried going back to implement MyTabViewController using Interface Builder so that I could set the buttons up in there, but I can't figure it out. These are the steps I took to get where I am:
Created new class descending from UIViewController in XCode, and checked the "with XIB" option.
Dragged a TabBarController into the XIB (the one with the yellow ball behind it).
The thing I can't figure out is how to get the TabBar to take over the view. Right now the View that comes with the XIB automatically is empty, and I have this UITabBarController that is totally disconnected from it. If I try to drag the TabBar from within the UITabBarController into the View, it appears to make a new TabBar instead of positioning my TabBar into the view so that it occupies the whole view.
I apologise if I haven't explained this very clearly, but I'm really struggling to understand the linkage between the TabBarController and the View, i.e. can't figure out how to get the TabBarController to actually display.
Any help with this would be much appreciated. I have attached a screengrab from IB if that helps at all.
alt text http://www.shelltoesmusic.com/files/tabbar_ib.png
The tab bar controller picks up the labels and images for the tabs from the view controllers it manages. The label comes from the view controllers' title. The image comes from the view controllers' tabBarItem properties. You can set both of these up in the init method of MyViewController1 and MyViewController2.
- (id)init {
if (self = [super initWithNibName:#"MyViewController" bundle:nil]) {
self.title = #"My View Controller";
UIImage* anImage = [UIImage imageNamed:#"MyViewControllerImage.png"];
UITabBarItem* theItem = [[UITabBarItem alloc] initWithTitle:#"Home" image:anImage tag:0];
self.tabBarItem = theItem;
[theItem release];
}
return self;
}
Every view controller has a view property. The tab bar controller is also a view controller so it too has a view property. Add the tab bar controller's view property to the view that is currently blank.
Example:
[view addSubview:tabBarController.view];
See documentation if you have questions. It's very complete.
Related
For the impatient:
I want to have a navigationcontroller who's root viewcontroller is a tabbarcontroller, similar to the iPad application. I am using IOS 5 and Storyboards.
For the reading inclined:
In my storyboard I have 6 tabs in a UITabBarController that is embeded in a UINavigationController, giving it a "More" button after 3 tabs are shown.
doing so gives me two navigation bars when more is pressed:
So I subclass TabBarController:
//#implentation MyTabController
- (void)viewDidLoad
{
self.moreNavigationController.wantsFullScreenLayout = NO;
self.delegate = self;
}
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController
{
// hide nav bar if current controller is "More" controller
self.navigationController.navigationBarHidden =
viewController == self.moreNavigationController;
}
Great, this gives me:
My guess was that i needed to relayout the views to account for the statusbar, so i try
[self.view setNeedsLayout:YES];
but i get an error saying UIView does not contain a selector for setNeedsLayout so...
How do I get the moreNavigationController.navigationBar to account for the statusbar?
Update:
I have a second related issue with this. When I hit the "Edit" button the edit controller shows modally. Its navigationbar displays underneath the insured controller (after an animation), and does not receive touches.
Pushing a tabBarController into a NavController isn't recommended, instead set a NavigatorController for every tabBar View controller, and set the TabBarController as the main window root view controller.
If you want to be able to show a screen before showing the tabbar, a solution is to push in all the navigator controllers the previous view controller, followed by the one you want to show (that way all navbars has the backbutton). Then set hidesBottomBarWhenPushed = YES to the first view controller, that way it won't show the tabBar.
Example Code:
UIViewController *prevc = [[UIViewController alloc] init];
//prevc.hidesBottomBarWhenPushed = YES;
//Do this for every VC that will be a tabBarItem
UIViewController *vc1 = [[UIViewController alloc] init];
UINavigationController *nv1 = [[UINavigationController alloc] initWithRootViewController:prevc];
[nv1 pushViewController:vc1 animated:NO];
//Remember to set the tabBarItem!
UITabBarController *tb = [[UITabBarController alloc] init];
tb.viewControllers = [NSArray arrayWithObjects:nv1, nv2, nv3, nil];
I just realized that setting hidesBottomBarWhenPushed to the previous ViewController won't work well, but If you show prevc first, and then push the following viewController, you won't have problems. But if anyway you wan't to hide the tab bar while doing a pop, please check this:
hidesBottomBarWhenPushed but when popped
I have also faced a similar problem. In my application also, there is a Tabarcontroller inside a Navigation controller. When i try to switch to a view controller in more navigation controller programatically (like : [self.tabBarController setSelectedIndex:X]; ) the same issues appears in my application. But the following code solves my problem.
self.tabBarController.moreNavigationController.navigationBarHidden = YES;
So before I've managed to work with TabBarViewControllers and create an application using them. However every time I do so the view acts as my main view. This time around I want my TabBarView to be my second view in my application
E.g
First window has a bunch of buttons, when I click one of these buttons I want the second view to show up. This view includes a TabBarViewController.
The farthest I've gotten is to have the button show a view but for some reason it won't show my TabBar view!
Here's the code for my button
- (IBAction)showEvents:(id)sender {
EventsViewController *controller = [[EventsViewController alloc] initWithNibName:#"EventsView" bundle:nil];
controller.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:controller animated:YES];
}
Any of you guys able to help?
Can't you just in the EventsViewController add the following code in viewDidLoad:
UITabBarController *tbc = [[UITabBarController alloc] init];
tbc.viewControllers = [NSArray arrayWithObjects: vc1, vc2, ..., nil];
Anyway, I found a solution and it was actually quite simple. After creating the Outlet for the TabBarController and linking it together with File's Owner all I had to do was add
self.view = tabViewController.view;
On the viewDidLoad method
my application needs the design similar to sybase ipad app which is here YouTube - Sybase Mobile Sales for SAP CRM on iPad
How can I set a tabbarcontroller as rootview controller of UISplitViewController.
When I try to do this, 8 tabitems are displaying without "More" button. so its overlapping items title. And it will display More button if more than 8 tab items.
As it is using width 320, How to set only 5 tabs visible at a time.
sample
array = [[NSMutableArray alloc] init ];
for(int i=0; i <10; i++){
TestTabController *cc = [[TestTabController alloc]init];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:cc] ;
[cc release];
[array addObject:navController];
[alertNavController release];
}
mytabBarController = [[UITabBarController alloc] init];
mytabBarController.viewControllers = array;
splitViewConntroller = [[UISplitViewController alloc] init];
[splitViewConntroller setViewControllers:[NSArray arrayWithObjects:mytabBarController, detailNavigationController, nil]];
How can I set the detailController view as a controller in the tabbarcontrolller(rootController) at runtime -Any easy methods ?-.. We can see when user tap on a cell in the detailController view, it immediatly move to rootController and its detail will show in detailController
Any help/comments/suggestions would be grealy appreciated.
If you create a UISplitViewController based project from the project templates and then open the MainWindow.xib file in interface builder, you can then just drag a tab bar controller component from the Library palette onto the first view controller (the navigation controller) inside the split view controller. Then you should be able to just start adding view controllers to the new tab bar controller. You then just specify the view controllers you want to use for each of the tabs. I've created several projects this way and it works quite well.
Of course, I'm assuming Xcode 3 here. I'm not completely sure if it's the same in Xcode 4 if that's what you're using.
if I'm not mistaken, UITabBarController should always be the root view controller in all cases.
So you're going the opposite way.
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];
}
So, I want my app starts with a UIViewController(without seeing a tabbar), and then enter a UITableView with navigationbar and tabbar. the problem is that the Tabbar is visible at the app starts up, anyone can help on this will be very appreciated...
I think you should either send -presentModalViewController:animated: to your main UIViewController with the tab bar controller as an argument or just do this:
[myWindow addSubview: myTabBarController.view];
Make your app a navigation based application (rather than a tab bar based one) then add a tab bar on the UITableView.
There is help for adding the UITabBar here
I do it like this : in this case drawing a table view and map view (From the Locati application)
tabBarController = [[UITabBarController alloc] init]; // creates your tab bar so you can add everything else to it
searchTableViewController = [[SearchTableViewController alloc] init]; // creates your table view - this should be a UIViewController with a table view in it, or UITableViewController
UINavigationController *searchTableNavController = [[[UINavigationController alloc] initWithRootViewController:searchTableViewController] autorelease];
[searchTableViewController release]; // creates your table view's navigation controller, then adds the view controller you made. Note I then let go of the view controller as the navigation controller now holds onto it
searchMapViewController = [[SearchMapViewController alloc] init];
UINavigationController *mapTableNavController = [[[UINavigationController alloc] initWithRootViewController:searchMapViewController] autorelease];
[searchMapViewController release]; // does exactly the same as the first round, but for your second tab at the bottom of the bar.
tabBarController.viewControllers = [NSArray arrayWithObjects:searchTableNavController, mapTableNavController, nil]; //add both of your navigation controllers to the tab bar. You can put as many controllers on as you like
I found this pattern a long time ago. Sorry that I can't point at the original.
YOu then need to add the tabbarcontoller to the relevant view ([...view addSubView:tabBarController];) possibly setting frame first.