Top Bar does not appear for presentModalViewController - objective-c

I've created a UIViewController subclass called addItemToListViewController. I selected add an "xib" as well, and just created a simple page with a couple of labels and a textField. In the interface builder I selected "Top Bar - Navigation Bar" so that when it is put on the stack when the application runs it will have a top bar that will match the initial main window. In the Interface builder it shows the top border, but when I run the application in the simulator the top bar is not present once the view is displayed.
Here is the code I placed in the rootViewController to present the view controller
- (IBAction)addButtonPressed:(id)sender
{
AddItemToListViewController *addItemToListViewController = [[AddItemToListViewController alloc] initWithNibName: #"AddItemToListViewController" bundle:nil];
[self presentModalViewController: AddItemToListViewController animated: YES];
[AddItemToListViewController release];
}
I'm only able to have the top bar present if I manually add a Navigation bar to the xib. If I must add a Navigation bar to my xib, what is the purpose of the "Top Bar" attribute?

- (IBAction)addButtonPressed:(id)sender
{
AddItemToListViewController *addItemToListViewController = [[AddItemToListViewController alloc] initWithNibName: #"AddItemToListViewController" bundle:nil];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:addItemToListViewController];
[self presentModalViewController: navController animated: YES];
[AddItemToListViewController release];
[navController release];
}

That "top bar - Navigation bar" in InterfaceBuilder is what's known as a "Simulated Metric". It's there to help you lay out your view with correct spacing when other visual elements - the status bar, navigation bar, or tab bar - might consume some of the device's screen real estate. It doesn't actually do anything other than shrink the vertical dimensions of the view defined by the NIB. The purpose is to help you layout your view, not to actually create a component that will appear in your app.
If you want a navigation bar, then you have two choices. The first choice is to use a navigation controller (of which your initial view will have to be the root) and call
[self.navigationController pushViewController:newVC animated:YES];
The process of setting up a navigation controller correctly, etc, is nontrivial, and you should do some searching to find the best way to do that for your app. For a simple app, especially if you're just learning iOS, you can use the "Navigation-based Application" template when you create a new project. With a navcon, you get all the fancy behavior normally associated with that top bar - an automatic back button, fancy left/right scrolling when you transition to a detail view, etc.
The second option is to put a "fake" navigation bar in the detail view, using the Navigation Bar object. You can find that object, plus some other related objects, in the bottom half of the "Utilities View" (the right-most pane) in XCode. Just drag the object into your XIB and blammo, you have a 44-pixel tall gray bar. This navigation bar is just like what you get when you use a Navigation Controller except you don't get the stack functionality; you can still add buttons to the left and right, change the title, tint it to a specific color, etc.

The xib does not know you will use the controller as a modal view as it could also be used for a normal view which could show a top bar. Only when you push the view it will use or ignore the showing of this top bar.
In short: its there in case you will use the xib for a normal view :)

Related

Stopping navigation bar from scrolling with table

I have a UITableViewController which successfully lists in the table view data fetched from a CoreData entity.
I want to have a navigation bar at the top with a couple of buttons to navigate around, but when I add the navigation bar it appears to sit inside the table, bellow any listings, and therefore scrolls wit the table when flicking through the table list.
As it is a UITableViewController I cannot make the table area smaller it seems and so I have no idea how to add the navigation bar outside of the table.
Any advise on how to make the fix the navigation bar in place so that it does not move.
Here is the code use to set the navigation bar:
- (void)viewDidLoad
{
UINavigationBar *navBar = [[UINavigationBar alloc] initWithFrame:CGRectMake(0,0, 1024,44)];
UINavigationItem *navItem = [[UINavigationItem alloc] initWithTitle:#"Observation details"];
[navBar pushNavigationItem:navItem animated:NO];
UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithTitle:#"Back" style:UIBarButtonItemStyleBordered target:self action:#selector(backButton)];
navItem.leftBarButtonItem = backButton;
UIBarButtonItem *detailsButton = [[UIBarButtonItem alloc] initWithTitle:#"Observation Details" style:UIBarButtonItemStyleBordered target:self action:#selector(detailsButton)];
navItem.rightBarButtonItem = detailsButton;
[self.view addSubview:navBar];
}
Don't drag the navigation bar and drop on the table view. That way you are setting that view (a navigation bar) as the header of the table view.
To do what you are trying to do, the easiest way is to use a navigation controller with the table view as its rootTableView.
myNavController = [[UINavigationController alloc] initWithRootViewController:myVC];
[self presentViewController:myNavController ...
You can also use a navigation bar without a navigation controller. For that, you have to set a View with a navigation controller at the top and a table view filling the rest of the view.
There are several ways to achieve this. I try to describe the one of which I believe it is easiest based on what I guess is your current level of experience.
Create a new empty view with a separate view controller.
Add two subviews to that view of the type container view. These views are designed to carry views that have their own view controllers. One of these containers is on top and has the size of the navigation bar and contains the navigation bar etc.
For that one you could create a separate view controller. However, I see no reason why that could not be the same view controller. (File's owner in IB)
It may well be a separate instance/class. That is up to you.
The remaining view is occupied by the second container view. This one contains a UITableView for which a UITableViewController (-subclass) is the File's Owner.
Unless you are more familiar with the view hierarchy and view controller hierarchy you should not break with the pattern (as given by default sceleton code and tutorials etc.) of having a 1:1 relation between table view controlers and table views. A UITableViewController has a view property as every UIViewController has. But for a UITableViewController that must be a UITableView and nothing else. The standard UITableViewController is not capable of dealing with a UIView as view property that contains a table view and other views as siblings. Plus it is not a good idea of adding more subviews to the table view with respect to its scrolling behaviour etc.
By separating them you can deal with the table and still take examples straigt of tutorial code plus you can have a navigation bar on top that is not influenced by the table at all.
However, I should mention that you could get all of this much easier by using a proper navigation controller. That would bring in your navigation bar for free. You must have good rasons for not doing so.

How to create a tab bar on iOS?

I need on my main view controller to have a tab bar with tabs to navigate to all my other controllers. I just need the tab bar on this controller and when i get to another controller i just need to have a back button to go to the main controller.
Now i have some questions. I created the tab bar in the main view controller and all the tabs with the text and images that i need. However i see that i can only create IBOutlet for the tab bar and not IBActions for every tab(as i thought). So i created an IBOutlet and connected it to my tab bar.
How can i refer to every tab?
If i can refer to every tab how is it possible to change the view controller when a tab is selected when i cant use an action about it?(I am not asking for the code to change controllers , i am asking for the place that i should put the code so that my application knows that this specific tab was pressed and has to change controller).
Thank you for reading my post :D
You can create a UITabBarController programmatically in applicationDidFinishLaunching and set it as the root view controller (or if you prefer, you can present it as a modal view). Here is the minimal code to do it:
UITabBarController *tabBarController = [[UITabBar alloc] init];
UIViewController *controller1 = [[YourViewController alloc] init];
UIViewController *controller2 = [[YourOtherViewController alloc] init];
tabBarController.viewControllers = [NSArray arrayWithObjects:
controller1,
controller2,
nil];
// set as the root window
window.rootViewController = tabBarController;
If you want to customize the look of the tab bar items, do so by adding overloading (UITabBarItem *)tabBarItem in the child view controller(s):
- (UITabBarItem *)tabBarItem
{
return [[UITabBarItem alloc] initWithTitle:#"Amazing" image:[UIImage imageNamed:#"Blah.png"] tag:0];
}
How to make a tab bar controller
by me
Drag tab bar controller into storyboard (hopefully you have one)
Control-drag from tab bar controller to each view you want hooked up to it
Pop bottles
Just so you know, this gives you the default tab bar controller behavior (so it will always be present and you can click from any page to another). If that's not what you want, then don't use a tab bar controller. To do otherwise is an abomination.
Storyboards are definitely helpful, but if you don't want to use one that's fine. Doing the Control Drag from the Tab Bar Controller to your new View Controller does indeed work (Dustin's response).

back button text does not change

For one of my view controller (extends UITableViewController), I need to configure its back button text to "Back". But the back button still shows up with parent view controller's title (the default).
- (void)viewDidLoad
{
...
self.navigationItem.backBarButtonItem = [[[UIBarButtonItem alloc] initWithTitle:#"Back" style:UIBarButtonItemStylePlain target:nil action:nil] autorelease];
}
You need to set the back button on the view controller that pushed the class onto the stack e.g. the controller that created and pushed the class you are showing
The docs for View Controller Programming Guide for iOS suggest that the UINavigationItems are kept in the Navigation Item Stack which provides the title and buttons for the current item on the stack.
Importantly (I added bits in [])
Although most of the navigation bar’s content is obtained from the topmost navigation item [in the navigation stack], a pointer to the back item [of the previous item in the stack] is maintained so that a back button (with the title of the preceding item) can be created.
Looking at the docs under the Configuring the Navigation Item Object section there is a diagram which shows the stack and the backItem pointing to the item below the top item in the stack. In your case the top item in the stack would refer to the UINavigationItem for the class you are showing and the backItem will be a pointer to the class that pushed it.
NB
Look at that section in the docs an image is worth a thousand words
Try this:
UIButton* backButton = [UIButton buttonWithType:101];
[backButton addTarget:self action:#selector(popView:) forControlEvents:UIControlEventTouchUpInside];
[backButton setTitle:#"Back" forState:UIControlStateNormal];
UIBarButtonItem* backItem = [[UIBarButtonItem alloc] initWithCustomView:backButton];
self.navigationItem.leftBarButtonItem = backItem;
self.navigationItem.backBarButtonItem.enabled = NO;
self.navigationItem.leftBarButtonItem.enabled = YES;
[backButton release];
You need implement a simple method popView, something like this:
-(IBAction)popView:(id)sender
{
[self.navigationController popViewControllerAnimated:NO];
}
Here's a bit more information from the latest UINavigationController class reference, which tells you how to change the button in either the "pushing" or "pushed" controller (I'd add the link, but Apple keeps moving stuff around in subtle ways. Please use your favorite search engine).
In summary: if you are configuring the back button in the pushing (ie, the previous) controller, you access the backBarButtonItem. This perfect for configuring a consistently named back button regardless of the next controller's type. If you want/need to change it in the pushed (ie, the current) controller, you need to access the leftBarButtonItem.
The relevant text is here:
The bar button item on the left side of the navigation bar allows for
navigation back to the previous view controller on the navigation
stack. The navigation controller updates the left side of the
navigation bar as follows:
If the new top-level view controller has a custom left bar button item, that item is displayed. To specify a custom left bar button
item, set the leftBarButtonItem property of the view controller’s
navigation item.
If the top-level view controller does not have a custom left bar button item, but the navigation item of the previous view controller
has a valid item in its backBarButtonItem property, the navigation bar
displays that item.
If a custom bar button item is not specified by either of the view controllers, a default back button is used and its title is set to the
value of the title property of the previous view controller—that is,
the view controller one level down on the stack. (If there is only one
view controller on the navigation stack, no back button is displayed.)

Adding a Navigation Controller to a View based Application adds top margin

I am trying to programmatically add a Navigation Controller to my View based Application. This is the code I am using (this code gets called after a button press in a view controller):
MainMenu *control = [[MainMenu alloc] initWithNibName: #"MainMenu" bundle: nil];
UINavigationController *navControl = [[UINavigationController alloc] initWithRootViewController: control];
[self.view addSubview:navControl.view];
[control release];
That works, but this ends up happening:
Notice the odd margin above the Navigation control.... My View controller that I am adding the Navigation Controller to has a gray background which you can see.
Any ideas??
If you have a better way of adding a Navigation Controller to a View based Application I am very open to suggestions!
Thank you in advance!
Thank you both for your response, but unfortunately, wantsFullScreenLayout set to YES or NO in the code didn't have any effect. I was able to push the Navigation Controller up by 20 using this line of code:
self.navigationController.navigationBar.frame = CGRectOffset(self.navigationController.navigationBar.frame, 0.0, -20.0);
but then what happened was that the View Controller did not move up with the Navigation bar and left a gap below the Navigation Bar and the View Controller. What eventually worked was checking the Wants Full Screen checkbox in IB in the MainWindow view controller that is automatically generated when you set up a view based application.
The gap you are seeing is the same height as a status bar. Check the status bar settings in your NIB file.
Chances are you want to make the UINavigationController the root view controller for the window, rather than whichever view controller you have now. That would be the better way to do it.
The reason you're seeing that extra margin at the top is because UINavigationController normally expects that it will be sized to fill the entire screen (except perhaps a tab bar at the bottom, if it's inside a UITabBarController), and therefore expects that the top edge of its view will be under the status bar if the status bar is visible. Therefore, it places its navigation bar 20 pixels below the top of its view to leave space for the status bar, without bothering to check whether its view actually is under the status bar. Interestingly, sometimes a re-layout operation will perform this check, but that's unreliable. What I've found works well in a situation like this is to set the UINavigationController's wantsFullScreenLayout property to NO. Then ti doesn't try to leave room for the status bar, so everything works as expected.
I've been struggling with this same issue this morning. Since setting the wantsFullScreenLayout property doesn't seem to have any effect, I resorted to using a little subclass, which worked fine:
#interface MyNavigationController : UINavigationController
#end
#implementation MyNavigationController
- (BOOL)wantsFullScreenLayout;
{
return NO;
}
#end
Its so simple to remove that gap..
self.navigationBar.view.frame = CGRectMake(0, -20, 320, 480);

UINavigationController without NavigationBar

I have a "landing page/view" that I dont want the navigation bar to show, so I turn it off during the viewDidAppear
navigationBarHidden = YES;
When i push a view on the stack and then move it off. the main landing page shows the nav bar then hides it which cause a flicker that I dont want.
is there a way to have the landing page be a UIView or something? When a menu item is touched the app would push a new view on top of the default landing page. It sound like it would be hard to do without having the landing page be a UINavigationController. Any thoguhts?
Try hiding the navigation bar in viewWillAppear, rather than viewDidAppear.
If you don't need to go back to the landing page, use a view controller for the landing page and present it modally from the navigation controller when the application starts.
So you do want to go back to the landing page.
It's hard to accomplish that with UINavigationController. Suppose your are going back to the landing view. While the transition, the old view should have a navigation bar, and the new view (landing page) should not have a navigation bar. UINavigationController does not allow you manually modifying the transition animation. In other words, you cannot animate hiding/unhiding the navigation bar along with push/pop animation (using viewWillAppear doesn't solve the problem).
So what would I do, if I really, really need this?
I would have a rootViewController (of UIViewController), whose view is the only subview of your application window. When your application starts, rootViewController add the landing view as a subview of its view. When the user selects an item there, you create an UINavigationController with the corresponding view controller as its root view controller.
And, using CATransition animation with type of kCATransitionPush and subtype of kCATransitionFromRight, you add the view of the navigation controller as a subview of rootViewController's view.
Then you need a 'back' button for the first view of the navigation controller. In all view controllers that are the first level view controllers of the navigation controller, create a bar button item with a text 'Back', and add it to their navigationItem.leftBarButton property. Set a target-action (probably to the rootViewController) pair for the button.
When the action message fires, use CATransition animation (now with kCATransitionFromLeft subtype), to remove the current navigation controller's view from rootViewController's view.
The transition may not look as perfect as the native UINavigationController, but I believe this is the best you could get.
Actually the way to do this is to implement UINavigationController's delegate method navigationController:willShowViewController:animated. This method is where you should handle hiding and showing your navigation bar so the animation will occur during the push/pop animation.
I came across a method that is simple and works well for me, and is not given here yet. I assume you have a view controller for the main landing page and it is set as root view controller of the navigation controller. Then you should hide/show the navigation bar in the viewWillAppear and viewWillDisappear methods of the main landing page controller:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self.navigationController setNavigationBarHidden:YES animated:YES];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[self.navigationController setNavigationBarHidden:NO animated:YES];
}
Source:
http://www.iosdevnotes.com/2011/03/uinavigationcontroller-tutorial/