I am trying to nest a UITabBar inside another one, like so:
UITabBarController *tabController = [[UITabBarController alloc] init];
tabController.delegate = self;
UITabBarController *friendsTabController = [[UITabBarController alloc] init];
FindFriendsViewController *findFriendsController = [[FindFriendsViewController alloc] initWithNibName:#"FindFriendsViewController" bundle:nil];
findFriendsController.rootViewController = self;
UINavigationController *findFriendsNavController = [[UINavigationController alloc] initWithRootViewController:findFriendsController];
findFriendsNavController.tabBarItem.title = NSLocalizedString(#"Add", nil);
friendsTabController.viewControllers = [NSArray arrayWithObjects:friendStreamController, friendListController, findFriendsNavController, nil];
tabController.viewControllers = [NSArray arrayWithObjects:nearbyNavController, friendsTabController, meController, checkInController, logController, nil];
(obviously, some of the code, such as the other tabs is ommitted for brevity)
When I do this, and click on the "Friends" tab of the outer tab bar, the inner tab bar appears directly above the outer one. I don't want this. I would like the inner tab bar to take the place (at the bottom of the window) of the outer one when it is selected.
I've tried doing this by hiding the outer tab bar, by setting it to hidden in the UITabBarController delegate method, and I've tried setting the frame of the inner tab bar to manually position it at the bottom of the screen like so:
friendsTabController.tabBar.frame = CGRectMake(0, 430, 320, 50);
When I hide the outer tab bar the inner one still appears the same distance from the bottom edge of the screen, and the outer one is replaced by a blank rectangle.
When I try setting the frame of the inner tab bar, the only part of it that is visible is any part that would be outside the frame of the outer tab bar.
I've tried googling this to no avail. Surely this kind of navigation is not so rare? Before attempting this I had assumed it would be supported in the SDK.
Any ideas?
TIA,
Tom
You could try having a single tab-bar controller in your app. When a user clicks on a tab that leads to a new set of tabs, replace all of the tabs in the single-tab-bar controller. I'm not sure you navigate "back" to the top level, but I presume you have something in mind.
I dont think this is great UI, but you can probably make it work this way.
I think a better solution to your problem would be to add a containerView to the viewController in the tab that you would like to nest the second tabBarController. Then you can add childViewControllers to that containerView and create a custom segue to those childViewControllers in much the same way a tabBarController does. This gives you the option to put your new custom "tab bar" ( I recommend a segmentedControl ) anywhere you like in the viewController containing the containerView. I think it's a more acceptable way to handle a nested tabBarController. Also the user will not be confused with a tabBar suddenly changing appearance.
Related
I've been requested to mock up an app with the following design.
The large gray box is the main view area. (UIView)
The 2 pink squares are buttons. They are almost like tabs of a tabbar in how they should function.
What I am struggling to achieve is to get 2 UINavigationbars/Controllers to appear in the UIView(large gray box) when a button is pressed. Not at the same time of course, but which ever Navbar/Controller/View that is shown is dependent on what button was selected.
I can make a UIViewControllers view appear there ok by doing the following
TabViewOne * vcTab = [[TabViewOne alloc] initWithNibName:#"TabViewOne" bundle:[NSBundle mainBundle]];
[self.mainView addSubview:vcTab.view];
That makes the view of TabViewOne appear in my mainView area (gray box).
What I would actually like to happen is that I can get my view to appear here but with a navbar and all the functionality that it brings. I will eventually just hide the nav bar but use its functionality to move up and down the view stack.
This is what I tried to get it working but the view stays blank when I try this :
TabViewOne * vcTab = [[TabViewOne alloc] initWithNibName:#"TabViewOne" bundle:[NSBundle mainBundle]];
UINavigationController * navVC = [[UINavigationController alloc] initWithRootViewController:vcTab];
[[navVC navigationBar] setHidden:YES];
[self.mainView addSubview:navVC.view];
Could somebody please advise me how to do this properly?
Many Thanks,
-Code
You can achieve your requirement from a small trick. You can implement a tab bar controller there. In tab bar controllers each tab can be run inside a separate navigation controller. In the root view controller of both the tab items you have to have that 2 button design. If you use a generic view and add it as a subview you can easily reuse it. Next thing is it will appear the tab bar at the bottom of the view as you have a tab bar controller there. You can hide it simply by making its frame rectangle to a non visible position. After that your tab selection should be done manually based on the users button click.
I would like to add a bottom bar in a view and then keep it there while I browse several views (each one with a nab bar). I hope it will follow Apple guidelines (I already know that only the nab bar is recommended) or at least the app is accepted.
For doing so, I have added a UIView to the application window. This UIView contains a UITabBarController which contains the navigation controllers (each one as a rootviewController) of each one of the items of the bar:
UIWindow *window=[[UIApplication sharedApplication] keyWindow];
MyUIViewController *mv=[[MyUIViewController alloc]init];
UINavigationController *navd = [[UINavigationController alloc] initWithRootViewController:mv];
navd.tabBarItem = [[UITabBarItem alloc] initWithTitle:#"MyItem" image:[UIImage imageNamed:#"myImage.png"] tag:0];
NSMutableArray *controllers = [[NSMutableArray alloc] init];
[controllers addObject:navd];
UITabBarController *tbarController=[[UITabBarController alloc] init];
tbarController.viewControllers = controllers;
tbarController.customizableViewControllers = controllers;
UIView *vistaBarraInferior=tbarController.view;
[window addSubview:vistaBarraInferior];
[window makeKeyAndVisible];
It works, but my problem appears when I would like to go back and exit the ivies with UITabBarController. If I write:
[self.tabBarController.view removeFromSuperview];
[self.parentViewController.navigationController popViewControllerAnimated:YES];
The tab bar is removed from the current view, but I canĀ“t reach the previous view (the root view I had had before doing anything), because I have overwritten it with the 'initWithRootViewController'.
Is it any other way to make it easier or make this work out?
Thanks in advance.
If you want to combine tab bar and navigation bar you must use tab bar as a root and for each tab item add one navigation controller. You can't do it in opposite way as tab bar must be always root.
I am not sure what do you want achieve with bottom bar. Whether it should serve as a tab bar or toolbar. Remember if you use tab bar then each tab will have its own stack of views managed by navigation controller. On the other hand toolbar is related to one view.
If you want to create something like overlay toolbar - something that is always on top of views even if they are animating in and out - you need to choose different solution.
For example you can create your own controller that will display your toolbar at bottom and some container view which content will be managed by navigation controller.
You should use a tabbarcontroller with array of navigation controllers. See apple sample code here for example.
I went through your code and it does not look right on different levels. For example you are calling initwithviewcontroller but passing a uiview. That is not correct.
If I rewrite this code in other way:
UITabBarController *tabBar=[[UITabBarController alloc]init];
tabBar.title=#"MyTitle";
NSMutableArray *items=[[NSMutableArray alloc]init];
MyUIViewController *ld = [[MyUIViewController alloc] init];
[tabBar addChildViewController:ld];
UITabBarItem *tabItem=[[UITabBarItem alloc]initWithTitle:#"Item1" image:[UIImage imageNamed:#"myImage.png"] tag:0];
[items addObject:tabItem];
[tabItem release];
[tabBar setToolbarItems:items];
[self.navigationController pushViewController:tabBar animated:YES];
The result is that the tab bar at the bottom is empty. Have I forgotten to write anything else to show the tab bar item?
If I add a second item, only its title is shown, but neither its image nor anything related to the first item is shown.
I have created a tabbarcontroller (with its default two view controllers) using interface builder in XCode 4.2.
But, when I run the application, the tab bar seems to be locked and I can't choose the other tab. Why is that?
PS: I haven't changed any property of the tab bar or tabbarcontroller in XCode.
How did you go about creating the tab bar? Did you have an initial view and then go to the Edit menu -> Embed In -> Tabbar Controller or did you start with nothing and drag in tab bar controller?
Either way, I just created a project with a single view and tried both ways - but the tab still worked. (if you do it by dragging the tab bar controller from the utility pane, you have to also select 'is initial view controller' if you replace the original view created with the project.
EDIT after your comments:
You don't really need to synthesize the tab bar controller in your AppDelegate - storyboarding will take care of this and you can reference it from code without needing generated synthesizers. Just design the layout in storyboard first by dragging in a tabbar controller (this will automatically create the two view controllers by default). Then select the tabbar controller and under the utilities panel, you'll see the 'is initial view controller' checkbox. Make sure it's checked. Then run your project.
Apologies - assumed because you were using XCode 4.2 that you were using storyboards. Perhaps you could try instantiating a tabbar controller fully in code instead of using IB at all. This is how I usually do it....
// Create the main tabbar controller
tabbarController = [[UITabBarController alloc] init];
// Create the first view controller
MyFirstViewController *v1 = [[MyFirstViewController alloc] initWithNibName:#"MyfirstViewController" bundle:nil];
[v1 setTitle:#"Tab1"];
// Create second view controller
MySecondViewController *v2 = [[MySecondViewController alloc] initWithNibName:#"MySecondViewController" bundle:nil];
[v2 setTitle:#"Tab2"];
// Make an array of controllers for the tabbar
NSArray *tabbarControllerArray = [NSArray arrayWithObjects:v1, v2, nil];
// Set the view controllers used by the tabbar controller
[tabbarController setViewControllers:tabbarControllerArray];
// Release views (retained elsewhere)
[v1 release];
[v2 release];
// Add the controller to the subview
[window addSubview:[tabbarController view]];
// Make key and visible
[self.window makeKeyAndVisible];
Give this way a shot and see how you get on.
I am new to iPhone development.
my application is based on UInavigationBar .
I want to add a navigation bar item in one of my xib, but in the .xib i just simulate the navigation bar so i can't drag and drop the item. thank you
You'll want to add the nav bar buttons programmatically. You see, your xib has a view that is shown within the content view of the UINavigationController. It is the UINavigationBar to which your app has access and which controls the nav bar items. As you point out, your xib has just a placeholder for the nav bar, which is really a convenience for you so your view is sized correctly as you lay it out.
In your UIViewController for the xib, you an add view-appropriate nav bar items with code something like
self.navigationItem.rightBarButtonItem = [[[UIBarButtonItem alloc]
initWithTitle:#"View" style:UIBarButtonItemStylePlain
target:self
action:#selector(viewControls:)] autorelease];
Does that make sense?
In order to be able to add items into a UINavigationBar, you need to first add a UINavigationBar to your view and then add items to it.
You cannot drag and drop items on a simulated navigation bar. A simulated Navigation Bar is just there to make sure you have a correct estimate of the view size available to you if you are adding a Navigation Bar by some other means or from code.
You should be using a UINavigationController for a navigation based hierarchy. That will take care of a lot of the lower details of how to make navigation work as you would like it to. I would also recommend setting that all up programmatically. Here is how you would do that.
// Initial setup of navigation
YourViewController *yvc = [[YourViewController alloc] init];
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:yvc];
[[self view] addSubview:[nav view]];
Then when you want to go to a new view controller (the animated sliding that you normally see), you do this
// From inside 'YourViewController',
// this is normally when the user touches a table view cell
NewViewController *nvc = [[NewViewController alloc] init];
[self.navigationController pushViewController:nvc];
If you want to change the title or the buttons, you do this
// This is normally in viewDidLoad or something similar
[self.navigationItem setTitle:#"Hello World!"];
[self.navigationItem.rightBarButtonItem:/* A UIBarButtonItem */];
I'm trying to implement a UI structured like in the Tweetie app, which behaves as so: the top-level view controller seems to be a navigation controller, whose root view is an "Accounts" table view. If you click on any account, it goes to the second level, which has a tab bar across the bottom. Each tab item shows a different list and lets you drill down further (the subsequent levels don't show the tab bar).
So, this seems like the implementation hierarchy is:
UINavigationController
Accounts: UITableViewController
UITabBarController
Tweets: UITableViewController
Detail view of a tweet/user/etc
Replies: UITableViewController
...
This seems to work[^1], but appears to be unsupported according to the SDK documentation for -pushViewController:animated: (emphasis added):
viewController: The view controller that is pushed onto the stack. It cannot be an instance of tab bar controller.
I would like to avoid private APIs and the like, but I'm not sure why this usage is explicitly prohibited even when it seems to work fine. Anyone know the reason?
I've thought about putting the tab bar controller as the main controller, with each of the tabs containing separate navigation controllers. The problem with this is that each nav controller needs to share a single root view controller (namely the "Accounts" table in Tweetie) -- this doesn't seem to work: pushing the table controller to a second nav controller seems to remove it from the first. Not to mention all the book-keeping when selecting a different account would probably be a pain.
How should I implement this the Right Way?
[^1]: The tab bar controller needs to be subclassed so that the tab bar controller's navigation item at that level stays in sync with the selected tab's navigation item, and the individual tab's table controller's need to push their respective detail views to self.tabBarController.navigationController instead of self.navigationController.
The two previous answers got it right - I don't use UITabBarController in Tweetie. It's pretty easy to write a custom XXTabBarController (plain subclass of UIViewController) that is happy to get pushed onto a nav controller stack, but still lives by the "view controller" philosophy. Each "tab" on the account-specific view (Tweets/Replies/Messages) is its own view controller, and as far as they are concerned they're getting swapped around on screen by a plain-ol UITabBarController.
I'm building an app that uses a similar navigation framework to Tweetie. I've written a post about how to do this on my blog www.wiredbob.com which also links to the source code. It's a full template you could take and use as a basis for another project. Good luck!
It's possible to add a UITabBar to any UIViewController. That way you don't actually have to push a UITabBarController and therefore stay within the guidelines of the Apple API.
In interface builder UITabBar is under "Windows, Views & Bars" in the Cocoa Touch Library.
I do this in a couple of my apps. The trick to adding a tab bar to a navigationController based app is to NOT use a TabBarController. Add a Tab Bar to the view, make the view controller for that view a TabBarDelegate, and respond to user selections on the tab bar in the code of the view controller.
I use Tab Bars to add additional views to the Tab Bar's view as sub-views, to reload a table view with different datasets, to reload a UIPickerView, etc.
I was struggling for the past hour to implement a UITabBar because it would get hidden when I tried to display my view; then I found this post:
Basically, make sure you insert your new view below the tabbar, per this line of code:
[self.view insertSubview:tab2ViewController.view belowSubview:myTabBar];
In my app, the root view controller is a UINavigation controller. At a certain point in the app, I need to display a UITabBar. I tried implementing a UITabBar on a UIView within the navigation hierarchy, as some of the previous posts suggested, and this does work. But I found that I wanted more of the default behavior that the tab controller provides and I found a way to use the UITabBarController with the UINavigation controller:
1) When I want to display the UITabBarController's view, I do this:
MyAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
appDelegate.window.rootViewController = myUiTabBarControllerInstance;
2) When I want to return to where I was in the navigation hierarchy, I do this:
appDelegate.window.rootViewController = myNavControllerInstance;
This could be achieved by simply embedding the TabBarController in the Navigation Controller.
In the storyboard:
Drag a ViewController
Click on the ViewController's Scene
Click on editor >> Embed in >> Navigation Controller.
Drag a button on the same ViewController.
Drag a TabBarController
Connect the button on the ViewController to the TabBarController via push Segue Action.
In this case only the TabBarController's RootViewController would be in the Navigation Controller's stack. All The TabBarItems would have the Navigation Bar at the top and user can go to Home Screen at any time, irrespective of the selected TabBarItem
This could be done at any ViewController in the Navigation Controller's stack.
If it works, please suggest me how to increase the reputation so that I can post the images and the code in the next answer. :)
This is how i did it. This is actually pushing a tabbarcontroller onto navigation controller. It works fine. I didn't find anywhere in the documentation that apple doesn't support this way. Can someone give me link to this warning?
If this is truth, is it possible that apple refuses to publish my app to appstore?
-(void)setArrayAndPushNextController
{
MyFirstViewController *myFirstViewController = [[MyFirstViewController alloc] init];
MySecondViewController *mySecondViewController = [[MySecondViewController alloc] init];
myFirstViewController.array = self.array;
NSArray *array = [[NSArray alloc] initWithObjects:myFirstViewController, mySecondViewController, nil];
UITabBarController *tab = [[UITabBarController alloc] init];
tab.viewControllers = array;
[array release];
UITabBarItem *item1 = [[UITabBarItem alloc] initWithTitle:#"first title" image:nil tag:1];
UITabBarItem *item2 = [[UITabBarItem alloc] initWithTitle:#"second title" image:nil tag:2];
myFirstViewController.tabBarItem = item1;
mySecondViewController.tabBarItem = item2;
[self stopAnimatingSpinner];
[self.navigationController pushViewController:tab animated:YES];
[tab release];
[item1 release];
[item2 release];
}
I wrote a blog post on how I approached this problem. For me, using a modal view was a simpler solution than writing a custom tab-bar implementation.
http://www.alexmedearis.com/uitabbarcontroller-inside-a-uinavigationcontroller/