Navigation Based Application with TabBar - iphone

I have a Navigation-Based Application that shows a TableView where you can select a cell and it brings you to a "Detail View" for that cell. I want this view to then have a TabBar where I can select between 3 subviews. I have found several solutions online for this but none are very helpful. Is there a tutorial for this specifically or is their source code indicating how it can be done? Thanks

Basically What you need to do is push a Tab View Controller onto the Navigation Controller's viewcontroller stack.
Starting with a fresh "Navigation-Based Application" template. I added the following method in RootViewController.m :
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
//Navigation logic may go here. Create and push another view controller.
UIViewController *viewOneViewController = [[UIViewController alloc] init];
viewOneViewController.title = #"One";
viewOneViewController.view.backgroundColor = [UIColor redColor];
UIViewController *viewTwoViewController = [[UIViewController alloc] init];
viewTwoViewController.title = #"Two";
viewTwoViewController.view.backgroundColor = [UIColor orangeColor];
UIViewController *viewThreeViewController = [[UIViewController alloc] init];
viewThreeViewController.title = #"Three";
viewThreeViewController.view.backgroundColor = [UIColor greenColor];
UITabBarController *anotherViewController = [[UITabBarController alloc] init];
anotherViewController.viewControllers = [NSArray arrayWithObjects:viewOneViewController, viewTwoViewController, viewThreeViewController, nil];
[self.navigationController pushViewController:anotherViewController animated:YES];
[anotherViewController release];
}
Changed this to 25 to test:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 25;
}
Now when I build and run I'll see what you are looking for in a basic way. What you will want to do after you get this working is to change the UIViewControllers to Custom Subclasses that you create to hold the code for each view. (If you are also using Interface Builder, change the init to initWithNibNamed:).
Hope this helps you get on your way a bit.

You should be aware that it's possible Apple will reject your application if you do this though.
From "Combining Tab Bar and Navigation Controllers"
It’s very common to combine tab bar and navigation controllers, as illustrated in Figure 4. To do this, you simply add navigation controllers to a tab bar controller (however, you should never add a tab bar controller to a navigation controller).
So it's likely to be against the Human Interface guidelines and so could you get rejected.

I'm trying to do this very same thing. The application "Tweetie" is doing something similar. They have a TableView of accounts and then you select an account and TabBar appears.
Anyways, straight from Apple's Documentation:
pushViewController:animated:
Pushes a view controller onto the receiver’s stack and updates the display.
(void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
Parameters
viewController
The view controller that is pushed onto the stack. It cannot be an instance of tab bar controller. This method does nothing if the view controller is already on the stack.
animated
Set this value to YES to animate the transition. Pass NO if you are setting up a navigation controller before its view is displayed.
So maybe we're just stuck using a TabBar and not a TabBarController?
-JP

Related

How to transition from one controller 1 to controller 2, and then have controller 2 immediately push another controller?

I have a MenuViewController and a UINavigationController sitting inside a ViewDeck, a framework for a side menu. You simply initiate like so:
ListingViewController* lvc = [[ListingViewController alloc] init];
UINavigationController* homeNavStack = [[UINavigationController alloc] initWithRootViewController:lvc];
MenuViewController* sideMenu = [[MenuViewController alloc] init];
IIViewDeckController* slideController = [[IIViewDeckController alloc] initWithCenterViewController:homeNavStack leftViewController:sideMenu];
Where the center view controller is my navigation controller, and the menu view controller is hidden on the left, and you must slide to the left to make it visible (similar to facebook's side menu).
There is a button in the side menu, that when pressed, needs to transition the app back to the navigation controller, and have it push a new view controller. Here is my code for this, inside MenuViewController.m:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
if(indexPath.row == 0){
NSLog(#"check");
UserProfileViewController* userProfile = [[UserProfileViewController alloc] init];
[self.viewDeckController toggleLeftViewAnimated:YES];
[self.centerViewController pushViewController:userProfile animated:YES];
}
}
toggleLeftViewAnimated brings back the center view controller, hiding the side menu again. I have given the side menu a reference to the center view controller, and using this, I ask it to push a new view controller. However when this method is called, nothing happens after the center view controller comes back into view. Does anyone know why this won't work?
Check whether the self.centerViewController is a Navigation Controller instance that you have added as center view controller. You need to get the instance of Navigation Controller you have added in to the IIViewDeckController where now you are using self (which might be the MenuViewController). I think you need to write a method to get the navigation controller form the IIViewDeckController and after that pushing your userProfile will work perfectly.

Custom TabBar navigation issues

I implemented a custom tab bar controller as a set of buttons each one related to it's own View Controller. I guided on this link http://idevrecipes.com/2011/01/04/how-does-the-twitter-iphone-app-implement-a-custom-tab-bar/ to achieve the behavior. So the relevant part of code is as follows:
- (void) selectedItemAtIndex:(NSUInteger)itemIndex
{
// Get the right view controller
NSDictionary* data = [self.tabBarItems objectAtIndex:itemIndex];
UIViewController* viewController = [data objectForKey:#"viewController"];
// Remove the current view controller's view
UIView* currentView = [self.view viewWithTag:SELECTED_VIEW_CONTROLLER_TAG];
[currentView removeFromSuperview];
// Set the view controller's frame to account for the tab bar (+ 48)
viewController.view.frame = CGRectMake(0,48,self.view.bounds.size.width, self.view.bounds.size.height - 48);
// Se the tag so we can find it later
viewController.view.tag = SELECTED_VIEW_CONTROLLER_TAG;
// Add the new view controller's view
[self.view insertSubview:viewController.view belowSubview:self.tabBar];
//Keep track of current view controller
self.currentController = viewController;
}
So far is working, I can see each view controller in a similar maner to the default TabBarViewController. But then there's a requirement where I need to push a new navigation controller modally (it should take all the application frame) from inside one of the tabBar controllers.
At first glance I tried the following code from within one of the tab controllers:
DetailViewController *detailViewController = [[DetailViewController alloc]init];
UINavigationController *navigationController = [[UINavigationController alloc]detailViewController];
[self presentModalViewController:navigationController animated:YES];
However is not working as expected, first the view is shown below the TabBar and second the new view is not taking in consideration the parent view frame which should be the screen bounds less the tabbar. (0, 48, 360, 412). My detail view controller it's loading content from a nib file.
Well, this is quite obvious since the TabBar Controller is inserting each view below my custom TabBar.
[self presentModalViewController:navigationController animated:YES];
So I tried inserting it directly as a window subview:
[[UIApplication sharedApplication].keyWindow addSubview:navigationController.view];
But, I think this is not okay... there should be a better approach that I can't figure out. So if anybody could give me suggestions on how to correct or improve this navigation system it would be great.
Thanks a lot.
If you are building you app for iOS 5.0 and up you can make use of childViewController. In your custom Tab Bar you can have a containerView and a tabView.
The view of viewController is added to containerView. All the necessary events are generated to the subsequently added viewController if the following methods are implemented correctly
- (void)addChildViewController:(UIViewController *)childController;
- (void)removeFromParentViewController;
More about viewController containment can be found here.

How to push DetailView without NavigationController on UIViewController

I have a ViewBased App. I added a UITableView on one of the UIViewControllers. It shows the data and I implemented all the delegate methods etc. My problem is when I want to show the detailView it just doesn't happen. My code:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
DetailViewController *detailViewController =[[DetailViewController alloc] initWithNibName:#"DetailViewController" bundle:nil];
NSLog(#"DidSelectRowAtIndexPath");
// Pass the selected object to the new view controller.
[self.navigationController pushViewController:detailViewController animated:YES];
[detailViewController release];
}
I see that I need a navigationController but I don't have one and I was unsucessful trying to add one programatically. I don't have one in my appDelegate either, so my question is do I need to add one to show the detail view? If yes, please give me a code sample how to do that.
If not, what other way is there?
I am new to iOS so I am a bit lost here.
Please help!
To add a navigation controller programmatically just for this detail view, you need to something like this:
UINavigationController * controller = [[UINavigationController alloc] initWithRootViewController:detailViewController];
[[detailViewController] release];
[self presentModalViewController: controller animated: YES];
If you want to use pushViewController, you need to already have a navigation controller surrounding the view you're starting with.
You need to add the Navigation Controller FIRST, then your master table becomes the root view controller of the nav controller, then when you tap a row in the table, you push another view controller onto the nav stack.
How does your master table get into the app in the first place? If you're using a nib, it's super easy to just change out the view controller for a nav controller with the old view controller added as a child of the nav controller.
You can create one programmatically by working within your app delegate's application:didFinishLaunchingWithOptions: method like so:
UITableViewController *tableViewController = [[[WhateverYourSubclassVCIsCalled alloc] init] autorelease];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:tableViewController];
window.rootViewController = navController;
[window makeKeyAndVisible];

How to nest a navigationController inside of a UIPopOverController?

There are actually three views at play. My homeView, which is where I launch the picker from, which sets the StudyPickerController view as root to a navController, which is then presented inside of the popOverController. Then from within the StudyPickerController view, I need to push to the ScreenPickerController, a different view completely.
I have a UIPopOverController that is displaying the contents of a view that is a tableView. I would like to be able to push a new view with a viewController inside of this view, but as I will discuss, it is really close, but it just won't push!
So from my homeView, when the button is pushed, an action is called and this code is run:
self.studyPicker = [[[StudyPickerController alloc] initWithStudyArray:self.studyArray ViewNum:butto.tag] autorelease];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:self.studyPicker];
_studyPicker.delegate = self;
self.studyPickerPopover = [[[UIPopoverController alloc] initWithContentViewController:navController] autorelease];
[self.studyPickerPopover presentPopoverFromRect:CGRectMake(120,45, 10,10) inView:self.view permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
And this works pretty well! The popOverController displays the contents of my StudyPickerController without any problems. I get the feeling that I am indeed getting access to my navigationController because the frame of the popOverController has a bar at the top, instead of just being a thin border, it has a navigationBar.
So now when I want to select a row in this view, I would like to push to a new view, also with a tableView, with my navigationController. This is the code:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSArray * array = [_studyArray objectAtIndex:indexPath.row];
ScreenPickerController * picker = [[ScreenPickerController alloc] init];
picker.seriesGUID = array;
picker.viewNumber = viewNumber;
[self.navigationController pushViewController:picker animated:YES];
}
It seems to me that this should be working! But alas, I press a row, and it highlights, and nothing happens.
I've been working on this all day, so it very well could be that I am just missing something, but I don't know what it is. Any help is appreciated
It seems there are two classes involved here: your homeView class and the StudyPickerController. I'm guessing the tableView:didSelectRowAtIndexPath: method would be in the homeView class?
In that case, self.navigationController is trying to access the navigation controller that homeView is in, not the navigation controller you put into the popover. Since homeView isn't even in a navigation controller, the accessor returns nil.
Instead, you will want to use something like [self.studyPicker.navigationController pushViewController:picker animated:YES];.

iPhone: Show modal UITableViewController with Navigation bar

I am showing a modal view which is a UITableViewController class. For some reason it won't show the navigation bar when I show it. Here is my code:
SettingsCreateAccount *detailViewController = [[SettingsCreateAccount alloc] initWithStyle:UITableViewStyleGrouped];
detailViewController.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
detailViewController.navigationController.navigationBarHidden = NO;
[self.navigationController presentModalViewController:detailViewController animated:YES];
detailViewController = nil;
[detailViewController release];
I thought it was shown by default? If it helps, I am calling this from another class that is also a UITableViewController managed by a UINavigationController. Ideas?
When you present a modal view controller it does not use any existing navigation controllers or navigation bars. If all you want is to display a navigation bar, you need to add the navigation bar as a subview of your modal view and present it as you're doing.
If you want to present a modal view controller with navigation functionality, you need to present a modal navigation controller containing your detail view controller instead, like so:
SettingsCreateAccount *detailViewController = [[SettingsCreateAccount alloc] initWithStyle:UITableViewStyleGrouped];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:detailViewController];
[detailViewController release];
navController.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentModalViewController:navController animated:YES];
[navController release];
Your modal controller will manage its own navigation stack.
Here is one way to display navigation bar for those who are using storyboards, suggested by Apple's Tutorial on Storyboard.
Because a modal view controller doesn’t get added to the navigation stack, it doesn’t get a navigation bar from the table view controller’s navigation controller. To give the view controller a navigation bar when presented modally, embed it in its own navigation controller.
In the outline view, select View Controller.
With the view controller selected, choose Editor > Embed In > Navigation Controller.
On iOS 7 and you just want a navigation bar on your modal view controller to show a title and some buttons? Try this magic in your UITableViewController:
// in the .h
#property (strong) UINavigationBar* navigationBar;
//in the .m
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationItem.title = #"Awesome";
self.navigationBar = [[UINavigationBar alloc] initWithFrame:CGRectZero];
[self.view addSubview:_navigationBar];
[self.navigationBar pushNavigationItem:self.navigationItem animated:NO];
}
-(void)layoutNavigationBar{
self.navigationBar.frame = CGRectMake(0, self.tableView.contentOffset.y, self.tableView.frame.size.width, self.topLayoutGuide.length + 44);
self.tableView.contentInset = UIEdgeInsetsMake(self.navigationBar.frame.size.height, 0, 0, 0);
}
-(void)scrollViewDidScroll:(UIScrollView *)scrollView{
//no need to call super
[self layoutNavigationBar];
}
-(void)viewDidLayoutSubviews{
[super viewDidLayoutSubviews];
[self layoutNavigationBar];
}
I want to share how the accepted solution can be used in projects with storyboards:
The simple approach is to put in a storyboard blank navigation controller before the VC which is to be presented modally, so the relations look like:
(Presenter VC) -> presents modally -> (navigation controller having a controller to be presented as its root).
We've tried this approach for a while and noticed that our storyboards become "polluted" by a large number of such intermediate navigation controllers when each! of them is used exclusively for one! presentation of some other controller, that we want to be presented modally with navigation bar.
Our current solution is to encapsulate the code from accepted answer to a custom segue:
#import "ModalPresentationWithNavigationBarSegue.h"
#implementation ModalPresentationWithNavigationBarSegue
- (void)perform {
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:self.destinationViewController];
[self.sourceViewController presentViewController:navigationController animated:YES completion:nil];
}
#end
Having this segue in our project we do not create intermediate navigation controllers in our storyboards anymore, we just use this ModalPresentationWithNavigationBarSegue like:
Presenter VC --> Presentee VC
I hope that this answer will be helpful to people who like to avoid unnecessary duplication in their apps storyboards.
I just wanted to add something to what #Scott said. His answer is definitely the easiest and most accepted way of doing it now with Storyboards, iOS 7 and 8... (and soon, 9).
Definitely adding a view controller to the Storyboard and Embedding it as described by #Scott is the right way to go.
Then, just add the segue by control-dragging from the source view controller to the target (the one you want to show modally), select "Present Modally" when the little view appears with the choices for the type of segue. Probably good to give it a name too (in the example below I use "presentMyModalViewController").
One thing that I needed that was missing is #Scott's case is when you want to actually pass on some data to that modally-presented view controller that is embedded in the navigation controller.
If you grab the segue.destinationViewController, it will be a UINavigationController, not the controller you embedded in the UINavigationController.
So, to get at the embedded view controller inside the navigation controller, here's what I did:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"presentMyModalViewController"]) {
// This could be collapsed, but it's a little easier to see
// what's going on written out this way.
// First get the destination view controller, which will be a UINavigationController
UINavigationController *nvc = (UINavigationController *)segue.destinationViewController;
// To get the view controller we're interested in, grab the navigation controller's "topViewController" property
MyModalViewController *vc = (EmailReceiptViewController *)[nvc topViewController];
// Now that we have the reference to our view controller, we can set its properties here:
vc.myAwesomeProperty = #"awesome!";
}
}
Hope this helps!
If you only need a NavigationBar, you can add an instance of UINavigationBar and assign BarItems to it.