I have a tab bar application and when I display a modal view controller, the content screen is offset by about 20 pixels to the top and left. It looks like this:
I'm presenting this view from the child view controller (detail view) of navigation controller (main view) of the tabview.
When I show the view, I'm hiding the tab bar and navigation bar but status bar is kept visible. Adjusting the view to be centered (through Interface Builder's Simulated Interface Elements->View Mode : Center) and changing the view's frame after a call to 'viewDidLoad' in the controller doesn't seem to shift it.
- (void)viewDidLoad {
// this still doesn't cause it to shift
self.view.frame = CGRectMake(0, 20, 320, 460);
}
What's the way to adjust this so that the content area is shown correctly?
I launch the child view like this:
[detailController presentModalViewController:tvc animated:NO];
The app's view controller hierarchy:
Tab view with two child navigation controllers are created in the app delegate and the nav controllers added to the TabBar's view controllers:
tabBarController = [[UITabBarController alloc] init];
tabBarController.viewControllers = [NSArray arrayWithObjects:tab1ViewController,
tab2ViewController, nil];
[window addSubview:tabBarController.view];
Each view controllers of the tab is created as a NavigationController with 1 view controller:
// MainViewController inherits from UIViewController
[MainViewController *viewController = [[MainViewController alloc] initWithNib..];
tab1ViewController.viewControllers = [NSArray arrayWithObject:viewController];
A detail view controller is launched with 'pushViewController' as a result of some action on tab1ViewController :
DetailController *detailController = [[DetailController alloc]
initWithNibName:#"DetailView"
bundle:[NSBundle mainBundle]];
[self.navigationController pushViewController:detailController animated:YES];
[detailController release];
It's from the detailController that I'm trying to launch the problem controller.
Some things to check right off: is "viewDidLoad" actually getting called?
If so, what is self.view.frame set to after the assignment?
Put an NSLog at the end that prints out the x, y, width, height, and see what's there.
Also, since the trouble vc is modal, it will occupy the entire screen.
"On iPhone and iPod touch devices, the view of modalViewController is always presented full screen."
HTH,
Mike
Related
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.
I am using UISplitViewController with UITabbarController as master view and navigation controller as detail view. Each of the tabs on the master view contain a navigation controller. When any of the tabs on the master view are selected, a view is pushed on the detail view navigation controller.
recentsviewController = [[RecentsViewController_ipad alloc]initWithNibName:#"RootViewController" bundle:nil];
recentsNav = [[UINavigationController alloc] initWithRootViewController:recentsviewController];
//similarly rootNav and favNav
NSArray* controllers = [NSArray arrayWithObjects:recentsNav,self.rootNav,favNav,nil];
self.tabbarController.viewControllers = controllers;
detailViewController=[[LoginViewController_ipad alloc]init];
detailNav=[[UINavigationController alloc]initWithRootViewController:detailViewController];
splitViewController =[[UISplitViewController alloc]init];
splitViewController.viewControllers=[NSArray arrayWithObjects:self.tabbarController,self.detailNav,nil];
splitViewController.delegate = self.detailViewController;
[self.window addSubview:splitViewController.view];
[self.window makeKeyAndVisible];
This seems to work fine. When app goes into portrait mode a 'Browse' button is added to the navigation bar. Clicking this button shows the hidden view along with the tabs perfectly fine. I can switch between the tabs and work on the views displayed in the popover. When tapped on other than popover area the popover is dismissed as expected.
The two things that are not working are
The popover has to show up on its own each time app switches to portrait mode without the user having to select the button added to nav bar
On some occasions I need to dismiss the popover programatically.
I have tried foll code :
- (void)splitViewController: (UISplitViewController*)svc willHideViewController:(UIViewController *)aViewController withBarButtonItem:(UIBarButtonItem*)barButtonItem forPopoverController: (UIPopoverController*)pc {
barButtonItem.title = #"Browse";
[[self navigationItem] setLeftBarButtonItem:barButtonItem];
self.appDelegate.rootPopoverButtonItem = barButtonItem;
self.appDelegate.splitViewPopover = pc;
[self.appDelegate.splitViewPopover presentPopoverFromBarButtonItem:self.appDelegate.rootPopoverButtonItem permittedArrowDirections:UIPopoverArrowDirectionUp animated:NO];
}
This adds a black translucent popover each time the orientation changes. How do I make it show what the browse button shows when it is clicked?
The usual dismissPopvoerAnimated does not seem to work here.
Any help is appreciated.
Using performSelector:withObject:afterDelay: fixed the issue
I was under the impression that a UINavigationController's navigation bar would always push down the child view's height, such that the child view's origin was at the bottom of the title bar.
But when I present a view controller like this ...
MyViewController *viewController = [[MyViewController alloc] init];
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:viewController];
viewController.title = #"My View Controller";
viewController.navigationItem.prompt = #"My Prompt";
viewController.delegate = self;
[self presentModalViewController:navigationController animated:YES];
[navigationController release]; [viewController release];
... and then check self.view.frame.size.height with an NSLog in viewDidLoad, it reports that my view is 460px high. Shouldn't it subtracting the height of my title & prompt?
So, as requested:
When you call viewDidLoad the view controller hasn't been pushed onto the screen yet. So when you get the frame size from within that method it will report its' default (typically, 320x480 for an iPhone app).
The view then autoresizes to take into account the navigation bar. So when you check the frame size in viewWillAppear it will now be correct. Typically this isn't a problem for iPhone apps. For iPad apps, where you have multiple orientations, it can be a bit of a pain!
There are a few exceptions to this - for example, when using NIBs.
Probably you are referring to the wrong self here.
When self refers to the MyViewController instance you will probably have an height of 460 - 44 pixels (standard UIToolbar height), but if self refers to a parent view controller (e.g. the navigation view controller itself) you'll see the standard view height (e.g. 460px).
You can check this by calling self.view.frame.size.height inside the instance of MyViewController, e.g. when the view has finished loading (viewWillAppear: method), and so has already been resized by its navigation controller.
If you call self.view.frame.size.height from the viewDidLoad method, the view probably has still not been resized by its parent controller.
Please try again and let me know if this fixes your problem.
My iphone application is showing strange behavior when rotating: a gap appears between the navigation title and content view inside a tab bar view (details on how to reproduce are below). I've created a tiny test case that exhibits the same problem: a custom root UIViewController, which creates and displays a UITabBarController programmatically, which has two tabs: 1) plain UIViewController, and 2) UINavigationController created programmatically with a single plain UIViewController content view.
The complete code for the application is in the root controller's viewDidLoad (every "*VC" class is a totally vanilla UIViewController subclass with XIB for user interface from XCode, with only the view background color changed to clearly identify each view, nothing else).
Here's the viewDidLoad code, and the shouldAutorotateToInterfaceOrientation code, this code is the entire application basically:
- (void)viewDidLoad {
[super viewDidLoad];
FirstVC *fvc = [[FirstVC alloc] initWithNibName:#"FirstVC" bundle:nil];
NavContentsVC *ncvc = [[NavContentsVC alloc] initWithNibName:#"NavContentsVC" bundle:nil];
UINavigationController *svc = [[UINavigationController alloc] initWithRootViewController:ncvc];
NSMutableArray *localControllersArray = [[NSMutableArray alloc] initWithCapacity:2];
[localControllersArray addObject:fvc];
[localControllersArray addObject:svc];
fvc.title = #"FirstVC-Title";
ncvc.title = #"NavContents-Title";
UITabBarController *tbc = [[UITabBarController alloc] init];
tbc.view.frame = CGRectMake(0, 0, 320, 460);
[tbc setViewControllers:localControllersArray];
[self.view addSubview:tbc.view];
[localControllersArray release];
[ncvc release];
[svc release];
[fvc release];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
Here's how to reproduce the problem:
1) start application
2) rotate device (happens in simulator, too) to landscape (UITabBar properly rotates)
3) click on tab 2
4) rotate device to portrait -- notice gap of root view controller's background color of about 10 pixels high beneath the Navigation title bar and the Navigation content view.
5) click tab 1
6) click tab 2
And the gap is gone! From my real application, I see that the gap remains during all VC push and pops while the NavigationController tab is active. Switching away to a different tab and back to the Nav tab clears up the gap.
What am I doing wrong? I'm running on SDK 3.1.3, this happens both on the simulator and on the device. Except for this particular sequence, everything seems to work fine. Help!
This problem occurs when you nest a UINavigationController within another UIViewController (in this case a UITabBarController). If you had the UINavigationController as the root view controller, then this problem wouldn't occur.
One solution may be to go in and alter the frame of the navigation bar (set the y origin from 0 to 20), but the documentation states explicitly not to do this. So to me, this is an indication that it isn't considered good UI to nest a UINavigationController - you shouldn't be doing it.
Please let me know what you think - thanks. :)
A workaround works in some occasion:
After rotating, force a refresh of the NavigationBar and therefore the frame of its view is resized properly. Some code like this:
(void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
// if _navigationController is showing
[_navigationController setNavigationBarHidden:YES];
[_navigationController setNavigationBarHidden:NO];
}
I'm writing a custom UITabBarController so I can fully control appearance of the tab bar. I've got it all working so I have an array of view controllers that it handles.
The controller has a main view which fills the screen, and inside it it has a UIView at the bottom for the tab bar. That tab bar view has a button for each view controller. When buttons are pressed I add the view controller's view to the main view, and set it's frame so that it doesn't cover the tab bar view:
controller.view.frame = CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height - kTabBarHeight);
This all works fine, and I can flick between the view controllers just fine. However, when I present a modal view controller, and then dismiss it, the current view controller's view becomes full screen and covers up my tab bar! I've tried setting the autoresizing masks to not resize, but is keeps happening.
I have also tried adding the view controllers view's to the bottom (below the tab bar) by using:
[self.view insertSubview:controller.view atIndex:0];
But when I do that, the tab bar is even visible above any modal views! Which is strange. I think there's something I'm not understanding so I would be grateful if someone can explain what I'm missing!
Thanks,
Mike
Try setting
controller.view.frame = CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height - kTabBarHeight);
in the controller's viewWillAppear method
Try this out. I think you want dynamic view controllers within tab bar controller.
-(void)applicationDidFinishLaunching:(UIApplication *)application {
// Add the tab bar controller's current view as a subview of the window
tabBarController.delegate=self;
tabBarController=[[UITabBarController alloc] init];
mainDashBoard=[[DashBoard alloc] initWithNibName:#"DashBoard" bundle:nil];
mainSearchView=[[SearchView alloc] initWithNibName:#"SearchView" bundle:nil];
mainMoreView=[[MoreView alloc] initWithNibName:#"MoreView" bundle:nil];
UINavigationController *nvCtr0=[[[UINavigationController alloc] init] autorelease];
UINavigationController *nvCtr1=[[[UINavigationController alloc] initWithRootViewController:mainDashBoard] autorelease];
UINavigationController *nvCtr2=[[[UINavigationController alloc] initWithRootViewController:mainSearchView] autorelease];
UINavigationController *nvCtr3=[[[UINavigationController alloc] initWithRootViewController:mainMoreView] autorelease];
UINavigationController *nvCtr4=[[[UINavigationController alloc] init] autorelease];//[[[UINavigationController alloc] initWithRootViewController:nil] autorelease];
tabBarController.viewControllers=[NSArray arrayWithObjects:nvCtr0,nvCtr1,nvCtr2,nvCtr3,nvCtr4,nil];
nvCtr0.tabBarItem.enabled=NO;
nvCtr4.tabBarItem.enabled=NO;
[window tabBarController.view];
}
I've managed to find a better way to control the appearance of the tab bar by simply inserting subviews to the top of the tab controllers tab bar. It's worked a treat!