I would like to combine UITableView and UINaviationController in an app but as a newbie most apps I've seen just send you straight to the results view (UITableView). But, I guess a "normal" search application does not assume you have the results on the first screen. There should be a search form on first screen with input fields and a button that triggers the search process and show some results and navigation.
So, I'm just trying to replicate this normal behaviour in my app. I've already made the search form (no navigation shown on it, of course) and a seperated View called "ListingViewController" with its related View and containing a UITableView and where I think I should add the Navigation...The next idea will be to make a DetailViewController and possibly and ListingMapController to show the listing in a GoogleMap.
So, where I'm stuck at is how to add this Navigation Controller ?
Some suggested me to add it in the SearchViewController delegate...
But I don't want a navigation on search form of course...
Some suggested me to open the Navigation controller modally...
But, I"m also planning at adding a Tab Bar to allow user to see other informations (like About,etc...) and with a modal Nav controller I don't know if they will still see the bottom Tabbar...
Any suggestions? What do you think is of best practices especially to avoid my app of being rejected by Apple?
Thx in advance for reading and helping!
Stephane
You could init the navigationController with your View Controller as the root view Controller. Then hide the navigationBar (if you need to). You would then add the navigationController.view as the subview. This will basically look like the original view controller. Then you can pushViewController: animated: to push the results view Controller.
So, for example in your AppDelegate (or in the proper view controller):
Create a property and ivar for a UINavigationController and hook up its outlets in interface builder. Then set your search controller as the root view controller for the nav bar, and add it as a subview.
MySearchViewController* searchController = [[MySearchViewController alloc] init];
self.myNavigationController = [[UINavigationController alloc] initWithRootController:searchController];
[searchController release];
self.myNavigationController.navigationBarHidden = YES;
[self.window addSubview:self.myNavigationController.view];
[self.window makeKeyAndVisible];
Then of course in your searchController, you would simply say:
ResultsViewController* myResultsViewController = [[MyResultsViewController alloc] init];
//You may want to create another init method and pass in some arguments like an array:
// [[MyResultsViewController alloc] initWithResults:results];
then push the viewController
//This is in your search controller class
[self.navigationController pushViewController:myResultsViewController Animated:YES];
[myResultsViewController release];
from the results viewController, to get back you pop the view controller off of the navigationController view controller's stack.
//In results view controller perhaps in some IBAction for a back button:
-(IBAction)backButtonPressed:(id)sender
{
[self.navigationController popViewControllerAnimated:YES];
}
Related
So I'm building an app and I am running through a few ViewControllers that don't need to know about each other, so I start off switching through views like so...
// remove the previous view in order to load in the new view
NSArray *sViews = [self.view subviews];
[sViews makeObjectsPerformSelector:#selector(removeFromSuperview)];
// create the new view, in this case the user wishes to
BaseViewController *baseVC = [[BaseViewController alloc] initWithNibName:#"BaseViewController" bundle:[NSBundle mainBundle]];
self.baseViewController = baseVC;
[baseVC release];
// add the newly created view to the screen
[self.view insertSubview:baseViewController.view atIndex:0];
The above is the view controller that I want the navigation controller to reside in. So within the .m of this view controller I created a UINavigationController as a member variable and named it navController. I then tried implementing a UINavigationController using the code below.
UIViewController *control = [[BusinessDisplayViewController alloc] initWithNibName:#"BusinessDisplayViewController" bundle: nil];
navController = [[UINavigationController alloc] initWithRootViewController:control];
[self presentModalViewController:navController animated:YES];
The problem I'm running into is two fold. First, when the BusinessDisplayViewController (below) is loaded there is a 20 pixel or so gap between my mapView and tableView that isn't there when I was loading it using insertSubview: not sure why that would be. Second, once I'm in BusinessDisplayViewController.m I'm not sure how to access the navigationController created in BaseViewController. Could someone explain why my view would be effected, how I could access the navigationController or if I'm even going about this the right way.
UINavigationController is designed for use in one of three possible contexts on iPhone:
As the app's root view controller, with its view added as a subview of the app's window.
As one of the viewControllers of a UITabBarController.
Presented as a full screen view controller via presentModalViewController:animated:.
In your case, the UINavigationController has configured itself for presentation as a subview of the window. This is why you see the 20 pixel gap at the top. Since the window object underlaps the status bar, UINavigationController offsets the position of its navigation bar by 20 pixels (or more, if you're on a phone call).
The standard way to use UINavigationController as your root view controller is to construct it as a property of your app delegate in application:didFinishLaunchingWithOptions:, and add its view as a subview of the window. Then within any view controller you push onto your navigation stack, you can access the navigation controller object using self.navigationController.
Usually, you want your UINavigationController to be at the root level, Is there a specific reason for having your app setup this way? To answer your question though, you can access the variable by setting a property for it, then using the dot notation: baseVC.navController.
For the 20 pixel space problem, post your BaseViewController view related code. It is probably a bounds vs frame issue.
I currently have an application that does the the following:
S: Loads a view as a login screen to start with.
a: If login is successful I add a terms and conditions screen as a subview
b: If not successful I add a sign up form as a subview
F: Then I load the main part of my app on success of either of the a or b which is the part of the app where there is a navigation controller and a tab bar controller. This is set up in MainWindow.xib
S, a and b also have Nav bars but no navigation controllers as I didn't think I would need navigation control on the login screens.
However it turns out I do, I want to be able to have back navigation from both a and b to the initial login screen.
I have tried several ways of doing this including trying the following answers:
How to add navigation controller in View Based Application in iPhone?
How do you properly set up a secondary view to support a navigation Controller on the iPhone?
how to add navigation controller programatically?
But none of them work for me, they display the new Navigation controller over the login screen and dont load the a or b screens.
I'm guessing this is because I am adding them as subviews to my loginView and this is not the correct way to do this? My code is as follows:
if(self.tcSubViewController == nil){
TCSubViewController *_tcSubViewController = [[TCSubViewController alloc] initWithNibName:#"T&CView" bundle:[NSBundle mainBundle]];
self.tcSubViewController = _tcSubViewController;
[_tcSubViewController release];
}
[self.view addSubview:[tcSubViewController view]];
I'm guessing there's a fundamental flaw in the way my Login flows? I should be able to completely remove the LoginView and then display the Terms and conditions view without having to add it as subview, shouldn't I?
You need to dismiss the navigation controller to go back to.
To dismiss modal view:
1.Easy way: In your modal view in some method that you call to dismiss just add:
[self.navigationController dismissModalViewControllerAnimated:YES];
2.More complex way: Implement and delegate protocol on your modal view and make the view controller that presents the modal view the delegate of it. And in the delegate method dismiss the modal view. I do this way when I need to send data from modal view to the controller that present it.
Reference to this post
Navigation controller philosophy is that you add only navigationController.view as UIWindow subview and it wil manage the rest by itself. You only need to push/pop viewControllers and their corresponding views will be added/removed from screen automatically.
sample code from my current application:
HomeController *homeController = [[[HomeController alloc] init] autorelease];
self.controller = [[[UINavigationController alloc] initWithRootViewController:homeController] autorelease];
self.controller.navigationBarHidden = YES;
[self.window addSubview:self.controller.view];
[self.window makeKeyAndVisible];
and then to push next view you just add next controller:
[self.navigationController pushViewController:newController animated:YES];
I have a view which contains a UIButton. When this is clicked, it calls a method that loads another NIB. Now, normally, that nib would load a view onto the stack, and everything would be fine. But, I am trying to load a Navigation Controller (so that I can have table views that are multiple levels deep), and all I get it errors.
What is the proper method for loading a Navigation Controller and putting it on the top of the stack?
As the other poster said you should create your Nav controller in your AppDelegate. If you are adding a new UIView to the stack like presentModalViewController you want to create the UIView then add the Nav Controller to it. If you don't want nav controller on that screen but the next just use the navController.hidden property I think it is.
To add the nav controller to the view do this:
NoticesView *noticesScreen = [[[NoticesView alloc] init] autorelease];
noticesScreen.delegate = self;
UINavigationController *navController = [[[UINavigationController alloc] initWithRootViewController:notices_screen] autorelease];
[self presentModalViewController:navController animated:YES];
Hope that helps towards your question. Still trying to find out exactly what your doing
Create the navigation controller in the app delegate. Push your mapview onto the stack as the first view. Push your tableview onto the stack as the second. If you started with a view-based app template, you won't have a navigationcontroller instantiated at all. (Been there done that) the easiest way out of this is to use xCode to make a navigation based applcation and then copy the code out of that. If you do already have a navigation controller, then just push the view controllers as above.
I would like to show a Navigation Controller after clicking a button. Every tutorial assumes the navigation controller will be the first screen so it links it to the App Delegate, but App delegate only appears at MainWindow.xib.
How do you guys add a navigation controller to a view different than the MainWindow?
Thanks!
Here is some sample code to expand on Roger's answer. The following method is linked to some user interaction on the current view controller (to compose an email for example). This will give the compose view the navigation bar across the top instead of coding buttons inside your custom view.
-(void) composeButtonPushed: (id) sender {
ComposeViewController *controller = [[ComposeViewController alloc] initWithNibName:#"ComposeView" bundle:nil];
UINavigationController *composeNavController = [[UINavigationController alloc] initWithRootViewController:controller];
[self presentModalViewController:composeNavController animated:NO];
}
UINavigationController is to navigate a heirarchy of views with UIViewControllers. If you don't have a root UIViewController, it won't work (and doesn;t make sense). If you do have a UIViewController, you simply send a - (id)initWithRootViewController:(UIViewController *)rootViewController init message to a new navigation controller passing in your UIViewController.
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/