How combine TabBar + Navigation with XCode - objective-c

I'm triying to combine a TabBar + Navigation app.
I have 5 tab bars, 4 are listing of stuff and drill down to details views.
I try to follow this tutorial:
http://www.iphonedevforums.com/forum/iphone-sdk-development/124-view-controller-problem.html
But always get a blank view.
This is what I do, with a clean project:
I start with a TabBar template app.
I put 5 tab bar buttons.
I create a controller like:
#interface FirstViewController : UINavigationController {
}
I put the main window.xib on tree mode & change the selected first view to FirstViewController
I select the TabBar Controller in Interface builder, go to TabBar Attributes & change the class to navigation controler.
Select the fist view & put the nib name "SecondView"
In response, I get a blank screen.
I must add that I wanna navigate from the details views, no from the main windows.
i.e in the main window tab bar 1 is the list of people. I select a person then wanna navigate to the detail window.

First, you never want to subclass UINavigationController or UITabBarController.
Second, I did not quite get what you did, but a correct sequence to create a navigation controller inside a tab bar controller is something like this:
// in MyAppDelegate.h
UINavigationController *nc1, *nc2;
FirstTabRootViewController *vc1;
SecondTabRootViewController *vc2;
UITabBarController *tbc;
// in MyAppDelegate.m
nc1 = [[UINavigationController alloc] init];
vc1 = [[FirstTabRootViewController alloc] initWithNibName:nil bundle:nil];
vc1.tabBarItem.title = #"Tab 1";
vc1.tabBarItem.image = [UIImage imageNamed:#"tab1.png"];
vc1.navigationItem.title = "Tab 1 Data";
nc1.viewControllers = [NSArray arrayWithObjects:vc1, nil];
nc2 = [[UINavigationController alloc] init];
vc2 = [[SecondTabRootViewController alloc] initWithNibName:nil bundle:nil];
vc2.tabBarItem.title = #"Tab 2";
vc2.tabBarItem.image = [UIImage imageNamed:#"tab2.png"];
vc2.navigationItem.title = "Tab 2 Data";
nc2.viewControllers = [NSArray arrayWithObjects:vc2, nil];
tbc = [[UITabBarController alloc] init];
tbc.viewControllers = [NSArray arrayWithObjects:nc1, nc2, nil];
Note that it's your view controller that controls the text/icon in the tab bar and in the navigation bar. Create a UINavigationController instance for each of your tabs; UINavigationController contains a stack of view controllers (see viewControllers property) which should contain at least one item — your root controller for that tab. Also create an UITabBarController to manage the tabs.
Of course, you can (and probably should) use interface builder instead of code to instantiate the mentioned classes and set the properties. But it's important that you understand what happens behind the scenes; interface builder is nothing more than a convenient way to instantiate and set up objects.
Hope this is helpful; please refine your question if it's not.

Still getting the blank screen On Starting the application after implementing the above code. Where i 'm writing it wrong.
nc1 = [[UINavigationController alloc] init];
nc2 = [[UINavigationController alloc] init];
vc1 = [[FirstRootViewController alloc]initWithNibName:#"FirstRootViewController" bundle:nil];
vc1.tabBarItem.title = #"Item 1";
vc1.tabBarItem.image= [UIImage imageNamed:#"home.png"];
vc1.navigationItem.title = #"Tab1 Data";
nc1.viewControllers = [NSArray arrayWithObjects:vc1,nil];
vc2 = [[SecondRootViewController alloc]initWithNibName:#"SecondRootViewController" bundle:nil];
vc2.tabBarItem.title = #"Item 2";
vc2.tabBarItem.image= [UIImage imageNamed:#"home.png"];
vc2.navigationItem.title = #"Tab2 Data";
nc2.viewControllers = [NSArray arrayWithObjects:vc2,nil];
tbc = [[UITabBarController alloc]init];
tbc.viewControllers = [NSArray arrayWithObjects:nc1,nc2,nil];
[window addSubview:tbc.view];
[window makeKeyAndVisible];

Here is an tutorial that I was able to get working.
I also read the official SDK documentation on the topic: Combining Tab Bar and Navigation Controllers. Since I'm still learning, the tutorial helped me more than the docs.
NOTE: in the tutorial, i don't think you need to subclass UINavigationController, and I'm experimenting with this idea now.

I tried to create an iphone application with UITabBarController and some UINavigationController inside it and faced the same problems as "mamcx". With your example-code i get it to run :) Thanks a lot.
Here is how it works for me.
// YOURS
fourthNavigation = [[UINavigationController alloc ] init ];
fourthViewController = [[[FourthTabRootController alloc] initWithNibName:#"FourthView" bundle:nil] autorelease];
fourthNavigation.tabBarItem.title = #"YOURS";
fourthNavigation.viewControllers = [NSArray arrayWithObjects:fourthViewController, nil];
// Add self-defined UIViewControllers to the tab bar
tabBarController.viewControllers = [NSArray arrayWithObjects:firstNavigation,secondNavigation, thirdNavigation, fourthNavigation, nil];
// Add the tab bar controller's current view as a subview of the window
[window addSubview:tabBarController.view];
The other UINavigationControllers "firstNavigation, ... " are created the same way.
I load the view elements from nib-files that are connected to my subclassed UIViewController classes. You dont need to add a NavigationBar in the IB to your view, cause the UINavigationController has allready one. So you only need to set the title in "initWithNibName"
// The designated initializer. Override to perform setup that is required before the view is loaded.
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
// Custom initialization
self.title = #"YOURS";
}
return self;
}
I hope that helps.

Check out "Adding a Navigation Controller to a Tab Bar Interface" under View Controller Catalog for iOS which take you step by step into how exactly this can be achieved

Related

How to navigate back from UISplitViewController IOS 5?

I have 6 views in which I am placing the UISplitViewController at 4th position (i.e.the 4th view). Now when I navigate from 3rd view to 4th view (i.e. ViewController to SplitView) I want to come back to 4th view to 3rd view (i.e. SplitView to ViewController).
I am using the below code to show the UISplitViewController when I am navigating from 3rd view to 4th view.
MasterViewController *masterViewController = [[MasterViewController alloc] initWithNibName:#"MasterViewController" bundle:nil];
UINavigationController *masterNavigationController = [[UINavigationController alloc] initWithRootViewController:masterViewController];
DetailViewController *detailViewController = [[DetailViewController alloc] initWithNibName:#"DetailViewController" bundle:nil];
UINavigationController *detailNavigationController = [[UINavigationController alloc] initWithRootViewController:detailViewController];
masterViewController.detailViewController = detailViewController;
self.splitViewController = [[UISplitViewController alloc] init];
self.splitViewController.delegate = detailViewController;
self.splitViewController.viewControllers = [NSArray arrayWithObjects:masterNavigationController, detailNavigationController, nil];
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication]delegate];
appDelegate.window.rootViewController = self.splitViewController;
Now I have created a toolbar in my navigation bar programmatically and placed a back button. Below is the code what I have tried to pop the view back to viewcontroller. (i.e. from SplitView to ViewController).
-(IBAction)backbtn:(UIBarButtonItem *)sender
{
// RepresentativeAccount<UISplitViewControllerDelegate> *represent = [[RepresentativeAccount alloc]initWithNibName:#"RepresentativeAccount" bundle:nil];
// //[self.navigationController popToViewController:[self.navigationController.viewControllers objectAtIndex:2] animated:YES];
// [[self.splitViewController.viewControllers objectAtIndex:0]popToViewController:represent animated:YES];
}
Please help thanks.
Reference: View Controller Catalog for iOS
A split view controller must always be the root of any interface you
create.
In other words, you must always install the view from a UISplitViewController object as the root view of your application’s window.
The panes of your split view interface may then contain navigation
controllers, tab bar controllers, or any other type of view controller
you need to implement your interface.
Split view controllers cannot be presented modally.
So, what you are attempting is incorrect, and in fact your app may get rejected by Apple.

Adding additional UITabbarItem to UITabbarController

I am wondering if there is a way to add an additional UITabBarItem to my exisiting UITabBarController. It doesn't need to be in runtime.
All I want to do is when hitting this button I want to presentModalViewController: over my actually visible ViewController, which should either be the TabBarController or its controllers.
Hopefully this is clear enough, if not, feel free to ask.
As a result of my research you cannot add a UITabBarItem to a UITabBar that is managed by a UITabBarController.
Since you maybe have added your UITabBarItem by adding a list of view controller, this is also the way of your choice to add further custom UITabBarItems, as i will show now:
Pre-Conditions:
As i mentioned before, you maybe have added your UITabBarItems by adding a list of view controller:
tabbarController = [[UITabBarController alloc] init]; // tabbarController has to be defined in your header file
FirstViewController *vc1 = [[FirstViewController alloc] initWithNibName:#"FirstViewController" bundle:[NSBundle mainBundle]];
vc1.tabBarItem.title = #"First View Controller"; // Let the controller manage the UITabBarItem
SecondViewController *vc2 = [[SecondViewController alloc] initWithNibName:#"SecondViewController" bundle:[NSBundle mainBundle]];
vc2.tabBarItem.title = #"Second View Controller";
[tabbarController setViewControllers:[NSArray arrayWithObjects: vc1, vc2, nil]];
tabbarController.delegate = self; // do not forget to delegate events to our appdelegate
Adding custom UITabBarItems:
Now since you know how to add UITabBarItems through adding view controller, you can also use the same way to add custom UITabBarItems:
UIViewController *tmpController = [[UIViewController alloc] init];
tmpController.tabBarItem.title = #"Custom TabBar Item";
// You could also add your custom image:
// tmpController.tabBarItem.image = [UIImage alloc];
// Define a custom tag (integers or enums only), so you can identify when it gets tapped:
tmpController.tabBarItem.tag = 1;
Modify the line above:
[tabbarController setViewControllers:[NSArray arrayWithObjects: vc1, vc2, tmpController, nil]];
[tmpController release]; // do not forget to release the tmpController after adding to the list
All fine, now you have your custom button in your TabBar.
What about handling events of this custom UITabBarItem?
Its easy, look:
Add the UITabBarControllerDelegate to your AppDelegate class (or the class which is holding the tabbarController).
#interface YourAppDelegate : NSObject <UIApplicationDelegate, UITabBarControllerDelegate> { }
Fit the protocol definition by adding this function:
- (void)tabBarController:(UITabBarController *)theTabBarController didSelectViewController:(UIViewController *)viewController {
NSUInteger indexOfTab = [theTabBarController.viewControllers indexOfObject:viewController];
UITabBarItem *item = [theTabBarController.tabBar.items objectAtIndex:indexOfTab];
NSLog(#"Tab index = %u (%u), itemtag: %d", indexOfTab, item.tag);
switch (item.tag) {
case 1:
// Do your stuff
break;
default:
break;
}
}
Now you have all you need to create and handle custom UITabBarItems.
Hope this helps.
Have fun....
Access the tabBar - property of your UITabBarController (reference), grab the elements array with the items - property (reference), add a new UITabBarItem to this array and use the tabBar's setItems:animated: - method to update your tab bar. Add an action to this tab bar to display the modal view controller.

Add button to my View "Header/Title" bar

In my AppDelegate, I create a UITabBarController and UINavigationController dynamically. I then add 3 views to it.
// Prepare the tab bar controller
tabBarController = [[UITabBarController alloc] init];
// Switch controller
UserSettingsController *settingsController = [[UserSettingsController alloc] init];
// Switches controller
SwitchesController *switchesController = [[SwitchesController alloc] init];
// Help controller
HelpController *helpController = [[HelpController alloc] init];
NSArray *controllers = [NSArray arrayWithObjects: switchesController, settingsController, helpController, nil];
tabBarController.viewControllers = controllers;
if (self.navigationController == nil) {
self.navigationController = [[UINavigationController alloc] initWithRootViewController:tabBarController];
}
[window addSubview:navigationController.view];
When I initially did this, I noticed that I now have a header/title bar at the top of my page. It's blank and really just takes up space. I'd like to utilize it though and add a "refresh" button to one of my views.
In the view I'm interested in, I attempted the following:
-(void)viewDidLoad {
[super viewDidLoad];
UIBarButtonItem *refresh = [[UIBarButtonItem alloc] initWithTitle:#"Refresh" style:UIBarButtonItemStylePlain target:self action:#selector(refreshSwitches:)];
self.navigationItem.rightBarButtonItem = refresh;
[refresh release];
}
-(void)refresh{
...
}
No button showed up, so I'm either way off or I'm missing something.
Any suggestions?
Thanks
I believe the problem is that your view controllers that you've added to the tabbar controller are not encapsulated by navigation controllers. Although your UITabBarController has a navigation controller, this does not implicitly give all of its tabs navigation controllers (and without a nav controller, a view controller's navigationItem won't do anything.) To remedy this problem I'd suggest encapsulating your view controllers with navigation controller upon initialization:
ex:
UserSettingsController *settingsController = [[UserSettingsController alloc] init];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:settingsController];
[settingsController release];
// etc...
NSArray *controllers = [NSArray arrayWithObjects:navController, ..., ..., nil];
tabBarController.viewControllers = controllers;
// Release your nav controllers, other cleanup
EDIT
Okay, I think I have a better idea of your setup now. I was able to get the following to work:
self.tabBarController.navigationItem.rightBarButtonItem = myButton;
An important caveat of this is that the button will persist on all of your tabbarcontroller's views unless you explicitly remove it. This may prove annoying/inelegant to maintain, but I am not sure of another solution if the view you want to have the button is not directly associated to a nav controller itself.

When "programmatically" creating UINavigationController and UITabBarController, how do I address their functions (like viewWillAppear?)

I am creating my Nav and TabBar in code at launch via:
IN : myAppDelegate.m
- (void)applicationDidFinishLaunching:(UIApplication *)application {
// set up a local nav controller which we will reuse for each view controller
UINavigationController *localNavigationController;
// create tab bar controller and array to hold the view controllers
tabBarController = [[UITabBarController alloc] init];
NSMutableArray *localControllersArray = [[NSMutableArray alloc] initWithCapacity:4];
// setup the first view controller (Root view controller)
RootViewController *myViewController;
myViewController = [[RootViewController alloc] initWithTabBar];
// create the nav controller and add the root view controller as its first view
localNavigationController = [[UINavigationController alloc] initWithRootViewController:myViewController];
// add the new nav controller (with the root view controller inside it)
// to the array of controllers
[localControllersArray addObject:localNavigationController];
// release since we are done with this for now
[localNavigationController release];
[myViewController release];
// setup the first view controller just like the first
ResortsListViewController *resortsListViewController;
resortsListViewController = [[ResortsListViewController alloc] initWithNibName:#"ResortsListView" bundle:nil];
resortsListViewController.title = #"Category1";
resortsListViewController.tabBarItem.image = [UIImage imageNamed:#"image1.png"];
resortsListViewController.navigationItem.title=#"Category1";
localNavigationController = [[UINavigationController alloc] initWithRootViewController:resortsListViewController];
[localControllersArray addObject:localNavigationController];
[localNavigationController release];
// setup the second view controller just like the first
ResortsListViewController *resortsListViewController;
resortsListViewController = [[ResortsListViewController alloc] initWithNibName:#"ResortsListView" bundle:nil];
resortsListViewController.title = #"Category2";
resortsListViewController.tabBarItem.image = [UIImage imageNamed:#"image2.png"];
resortsListViewController.navigationItem.title=#"Category2";
localNavigationController = [[UINavigationController alloc] initWithRootViewController:resortsListViewController];
[localControllersArray addObject:localNavigationController];
[localNavigationController release];
// setup the third view controller just like the first
ResortsListViewController *resortsListViewController;
resortsListViewController = [[ResortsListViewController alloc] initWithNibName:#"ResortsListView" bundle:nil];
resortsListViewController.title = #"Category3";
resortsListViewController.tabBarItem.image = [UIImage imageNamed:#"image3.png"];
resortsListViewController.navigationItem.title=#"Category3";
localNavigationController = [[UINavigationController alloc] initWithRootViewController:resortsListViewController];
[localControllersArray addObject:localNavigationController];
[localNavigationController release];
[resortsListViewController 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
[window addSubview:tabBarController.view];
// need this last line to display the window (and tab bar controller)
[window makeKeyAndVisible];
}
As you see, I am re-using ResortsListViewController for different category displays (resorts with Beaches, resorts with Pools, resorts with espresso bars) ... now, without harassing me (grin) about the silliness of my categories (cos this is a test app) I need need to do several things:
I need to be able to know which tab click caused the ResortsListViewController to be displayed. I was hoping to use TAG but "initWithRootViewController" does not have the "tag" control. So, if i use an imagefilename that is the category name, I can use that filename to distinguish categories...or even navigationItem name. I need to know if there is a way for ResortsListViewController to know which tabbar item click caused it's display. I thought to look for a "action" that I could assign to the tabbar item, but that is not the way tabbarcontroller works.
When clicking from one tab to another, the view does indeed change, the title of ResortsListViewController changes, etc...but the TABLEVIEW it holds does not clear and display any new data. Searching the web I have found a possible solution:
http://discussions.apple.com/thread.jspa?threadID=1529769&tstart=0
basically saying:
In order for UINavigationControllers
to send
"viewWill/Did/Appear/Disappear"
messages, it needs to have received
"viewWill/Did/Appear/Disappear" from
its container.
What is the container for my UINavigationControllers in this situation? myAppDelegate is defined in the .h file as:
NSObject <UIApplicationDelegate, CLLocationManagerDelegate>
and does not have a:
- (void)viewWillAppear:(BOOL)animated {
}
section. When I add one it says "NSObject may not respond to -viewWillAppear" in the debugger.
Any help out there?
1) This is fairly simple. You need to set the delegate property for the UITabBarController. You would set this to the controller object that owns your tabbarcontroller (or your app delegate if that is your setup). Whichever object is set as the delegate would then receive:
- (BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController
AND
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController
to manage tab selections. Place one of these methods in the implementation of whatever object you set as the delegate (you would want it to be the object that owns all of the controllers).
2) If you have a UIViewController that appears as a tab selection (such as ResortsListViewController) then you would need to put the viewWillAppear method in the controller implementation yourself:
#implementation ResortsListViewController
- (id)initWithNibName:(NSString *)name bundle:(NSBundle *)bundle {
...
}
... etc. ....
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[table reloadData];
}
...
#end
Let me know if I misunderstood the second part of your question.
Okay, here goes: This is the correct answer to the question, however, it did not end up being this hard. All I had to do was the following:
Create a property in ResortViewController of type int with variable name whichChoice (for instance). Then address it in the setup of the TabBarController a la:
// setup the first view controller just like the first
ResortsListViewController *resortsListViewController;
resortsListViewController = [[ResortsListViewController alloc] initWithNibName:#"ResortsListView" bundle:nil];
// START HERE IS THE CHANGE
resortsListViewController.whichChoice = 1;
// END HERE IS THE CHANGE
resortsListViewController.title = #"Category1";
resortsListViewController.tabBarItem.image = [UIImage imageNamed:#"image1.png"];
resortsListViewController.navigationItem.title=#"Category1";
localNavigationController = [[UINavigationController alloc] initWithRootViewController:resortsListViewController];
[localControllersArray addObject:localNavigationController];
[localNavigationController release];
To find out which tab was clicked when my resortsListViewController takes over, I simply query the class variable: whichChoice to get the answer.
Things like this are so simple you skip over them. I thought you had to pass the variable in an action and specify where it would go with the target like you do in other objects -- but when you set things up ahead of time you do not have to do that. Now, having said that, dynamically setting "whichChoice" is not so easy without a lot more thought...but setting it to a variable KNOWN at setup is fine.
For answer #2, I simply put a variable style of IBOutlet in my class, hooked it up to the table, and then followed your instructions, because without the variable in the IBOutlet and hooking it up to the table, there is no reference to the table view. For some reason hooking up the table simply to the VIEW "Referencing Outlet" and calling [self.tableview reloadData] did not do the job.
But for the most part, your answers were right and led me in the right direction. As an aside, I really hate that you have to right-click and drag from here-to-there in IB if you have an IB element you built. You should be able to hook it up in code using it's ObjectId (or something). That would be more in line with programmers. I know IB is made to allow programming to be easier for designers, but gee wilikers..it is hard to wrap my mind around! I end up dropping IB and creating elements in code most of time time...which I do not know if is as fast to execute. I tend to think not...but have no proof to the contrary.

UITableView+UINavigationBar+UITabbar in windows based application template using interface Builder?

i want a view with UITabBAr at The bottom with some buttons+Navigation controller on each view and a tableview inside the view using interface builder and windows basaed application template. i want a step by step guide to do this
This is very simple. Just create your appdelegate with this function:
- (void)applicationDidFinishLaunching {
TableSubclass *tvc = [[TableSubclass alloc] init]; //subclass of UITableView you declare
UINavigationController *navBar = [[UINavigationController alloc] initWithRootViewController:tvc];
navBar.tabBarItem.title = #"Foo";
NSArray *tabViewControllerArray = [NSArray arrayWithObjects:navBar, nil];
self.tabBarController.viewControllers = tabViewControllerArray;
self.tabBarController.delegate = self;
[tvc release];
}
Now, just create your UITableView subclass and you are all set.
If you want multiple views, and not just one table, you can just declare more view controllers and add them to the tabViewControllerArray.