UINavigationController push viewcontroller with hidden navigationbar - ios

I have a UINavigationController and its navigationBar hides if the user scrolls (done with UIScrollViewDelegate, not with pan gesture).
But when I push a a new viewController onto the stack, the new controller's view's origin y is at 64px and not where the old topViewController's y was (which is 21px).
How can I make the push, so that the new controller appears at the same position as the old one was?
The only place that updating the view's frame was successful was in viewDidAppear, but that's to late, because the user can see a black space on the top for a second.
Updated:
It seams like it has to be done between viewWillAppear and viewDidAppear, but I can not find reasonable method to do this.
Tried to alter the UINavigationController's view (currently I'm just moving it's navigationBar), but that messes up the layout.
Update again:
Some more info, because none of the answers and comments helped. The navigationcontroller is instantiated from storyboard. It's a custom UINavigationController, which hides the navigationBar on it's topViewController's scrollView scrolling (nothing else is overwritten).
The first topViewController (from which the push is started) is a UIViewController with 2 container view (one if logged in, other if not). The second topViewController (the one which is pushed onto the stack) is a UIViewController with a full screen UIWebView.
Everything is coming from the storyboard. All the Extended Edges checkboxes are off for every viewController and all the navigationBars are opaque. AutoLayout is used everywhere and (I think setup right).

--Edited--
change your frame in viewWillAppearmethod
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self.view setFrame:CGRectMake(0, 21, self.view.frame.size.width, self.view.frame.size.height)];
}
or setFrame of your newController before to be pushed.
NewController *newC = [NewController alloc] initWithNibName:#"NewController" bundle:nil];
[newC.view setFrame:CGRectMake(0, 21, self.view.frame.size.width, self.view.frame.size.height)];
[self.navigationController pushViewController:newC animated: YES];

Related

Frame of main View of UIViewController inside a UINavigationViewController

I am confused by the frame of self.view within a ViewController I have made the root ViewController of a UINavigationViewController. Lets say I add a red square UIView to self.view (where self.view is the embedded ViewController's view property). This red view is mostly occluded by the navigation bar. In other words, why doesn't self.view's frame's origin.Y = HeightOfNav bar? Instead it is 0.
I have read documentation that states a view added to the navigation controller resizes to take the nav bar's height into account, however, all my testing has shown this isn't the case. Not at any point in the view lifecycle does my view correct its origin to be visible. Perhaps I am setting up this viewcontroller/navigationController incorrectly such that the appropriate view-resizing does not take place but you can read the subsequent code to see if that is the case.
Here is the simple code where I generate these view controllers in appdelegate:
UIViewController * a = [[ViewController alloc] init];
UINavigationController * nav = [[UINavigationController alloc] initWithRootViewController:a];
self.window.rootViewController = nav;
Logging out self.view's frame in a's viewDidLoad, viewWillAppear, viewDidAppear, etc.. shows { 0, 0, screenWidth, screenHeight } (where screen* is the current simulator). So, 0,0 is behind a navBar and this is not the behavior I would expect.
Again, my expectation was that self.view would now have a frame whose origin starts after the NavigationBar at the top of any view inside a UINavigationController.

pushViewController:animated: doesn't fully animate current controller out of bounds

I have a UINavigationController thats width is 255 and the rootViewController is a UITableViewController that is of the same width. The problem is that when I push a new view controller, the current rootViewController animates about 1/3 of the way off screen and then stays visible while the new controller comes in and then it disappears. If I set animated to NO it works fine, but I need to animate it. There isn't anything fancy going on in the code, it is a simple push/pop setup when switching controllers. I added a video below to show you what it looks like. I put borders on the navigation controller (blue) and table view (orange). The code that does the push/pop is:
if ([[self.menuNavigationController topViewController] isKindOfClass:[TeamMenuViewController class]])
{
[self.menuNavigationController pushViewController:self.teamsViewController animated:YES];
}
else
{
[self.menuNavigationController popToRootViewControllerAnimated:YES];
}
It looks like your viewController.view that is being pushed on has a transparent background. The default UINavigationController animation in iOS 7 and 8 has an overlap effect.

Force UINavigationBar to overlay container below instead of pushing it down

I was just wondering if there is a simple way to specify whether a UINavigationBar should overlay its content when shown. I currently have a UINavigationController that contains a custom UIViewController with a UIScrollView, which contains a UIPageViewController (I wanted a zooming/scrollable UIPageViewController).
When I call:
[self setNavigationBarHidden:NO animated:YES];
From within my UINavigationController, the UINavigationBar animates in, but pushes the custom container with all its content down, instead of overlaying it.
The bar is set to translucent and I've tried all the settings I can think of. I changed the extendEdges settings in the child view controllers and that resized the content when the navigation bar came in, instead of pushing it down. But I still can't work out how to get it to overlay instead.
Many thanks.
Ok, apologies. This just reveals how little I know about iOS programming...
I noticed that the container's view origin y value was -44. So adding the following within my container class:
- (void) viewDidLayoutSubviews
{
CGRect boundsRect = self.view.bounds;
boundsRect.origin = CGPointMake(0, 0);
self.view.bounds = boundsRect;
}
Results in the view staying at the top of the screen, so the UINavigationBar overlays it nicely when it appears.
EDIT: Actually the proper way appears to be just calling:
self.automaticallyAdjustsScrollViewInsets = NO;

UIView in UITabBar don't extend to full

I have a UIViewController called DashBoardViewController that acts as delegate for a UITabBar. In its xib I have placed a UITabBar with 3 UITabBarItem.
Each of these items activate a different View Controller, let's call them ViewController1, ViewController2, ViewController3
DashBoardViewController is supposed to show ViewController1 and select the first bar on loading, so in my initWithNibName I have what follows:
...
ViewController1* vc = [[ViewController1 alloc] initWithNibName:#"ViewController1" bundle:nil];
[self.view addSubview:vc.view];
self.currentViewController = vc;
...
I implement the UITabBarDelegate having something as follows:
if (item == viewController1Item) {
ViewController2 *vc2 = [self.childrenControllers objectAtIndex:1];
[self.currentViewController.view removeFromSuperview];
[self.view addSubview:vc2.view];
self.currentViewController = vc2;
} ...
Problem
The View Controller in the first UITabBarItem always works as expected, extending it to the full size of thew view.
However, in the second and following tabs, this doesn't happen: the view doesn't extends. This shows if, for example, I align a tab with the bottom in the ViewController2 XIB: this will not be at the bottom when viewed inside the UITabBarItem.
Note
Please note that this is not related to the XIB: if I invert ViewController1 and ViewController2, it will be ViewController1 the one failing to extend. It's related to the UITabBarItem.
Ideas
Possibly, this depends by the way I addSubview when I call the DashBoardViewController's initWithNibName. But I can't find a way to explain this.
Other details
All the XIB are set with "Size = none".
I can't really speak to the way you have your XIB setup without seeing it, but I can make a couple of suggestions.
The behaviour that you're trying to implement by removing & adding subviews to DashBoardViewController should really be handled by a UITabBarController. This provides a UITabBar, a view for your content and handles the logic of switching between UIViewControllers while keeping layout sane and being part of the SDK.
If for some reason you can't, or don't want to use a UITabBarController, I'd suggest implementing a viewWillLayoutSubviews method on your DashBoardViewController, like so:
- (void)viewWillLayoutSubviews
{
if( self.currentViewController )
{
self.currentViewController.view.frame = self.view.bounds;
}
}
Maybe also try adding the self.currentViewController.view.frame = self.view.bounds; line after you've swapped ViewControllers too, for good measure. This will make sure that the frame of your current ViewController's view is always sized to fill the bounds of DashBoardViewController's view.
This isn't the 'Proper' way to do it though, I'd really recommend using a UITabBarController if you can, since you don't know how much else of UITabBarController you'll end up re-implementing if you start rolling your own controller.
Any further problems will most probably be to do with the internal layout of your sub-ViewControllers, rather than their size / position in DashBoardViewController's view.
On your XIB File make sure that your set the flexible height to stick to top and bottom, this way the UITableView will always have the same height as the 4" display

UINavigationController has extra status bar gap at top

This looked simple enough when I set it up, but I can't explain why this gap is present between the status bar and the navigation bar. Also, the contained view looks like it may be properly aligned, and it's just the nav bar that is shifted down. The gap looks like the size of the status bar, so I expect that has something to do with it, but I don't know what.
Here is the code for setting up the navigation controller:
- (void)viewDidLoad
{
[super viewDidLoad];
advancedVC = [[AdvancedSearchFormVC alloc] initWithNibName:#"AdvancedSearchForm" bundle:nil];
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:advancedVC];
nav.navigationBar.tintColor = [UIColor defaultNavBarTint];
nav.navigationBar.topItem.title = NSLocalizedString(#"SearchTitle", nil);
UIBarButtonItem *searchButton = [[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(#"SearchButton", nil) style:UIBarButtonItemStylePlain target:self action:#selector(refreshPropertyList:)];
nav.navigationBar.topItem.rightBarButtonItem = searchButton;
self.view = nav.view;
}
The rootViewController uses a view from a xib file, where I have simulated the status bar, the navigation bar, and the tab bar.
The problem is indeed that the navigation controller always expects to leave room for the status bar, which is the 20 pixel gap. I searched around for a while before I found this solution which works:
//nav is assumed to be a subclass or instance of UINavigationController
nav.view.frame = CGRectOffset(nav.view.frame, 0.0, -20.0);
//you can then add the navigation's view as a subview to something else
I originally found an answer which did this offset to the navigationbar's view, but it didn't work. It works when you do it to the navigation controllers actual view.
I use this technique to add a navigation controller from another nib to an empty view of my main nib, so I can position it anywhere within the main screen as a subview. By using an empty view as a placeholder and positioning frame on my main nib, I create a separate nib and class to manage the navigation, which manages other nibs used to handle their screens. This way I can solve the classic "how do I add a banner, image, or custom views above my navigation controller" while having a navigation controller as a subview...in iOS 5 to be specific.
It's also worth mentioning that I use the app delegate to store and access all the other controllers, so they are retained by a persistant instance which I can access from any class. Create and synthesise some properties in the app delegate of all your controllers, and in viewDidLoad create instances. That way I can reference all the controllers used in my app later anywhere by adding:
//this shows how to store your navigation controllers in the app delegate
//assumes you've added 2 properties (UINavigationController*)"navController" and (UIViewController*)"rootController" in your app delegate
//...don't forget to add #import "AppDelegate.h" to the top of the file
AppDelegate *app = (AppDelegate*)[[UIApplication sharedApplication] delegate];
[app.navController pushViewController: app.rootController animated:YES];
//now apply the offset trick to remove the status gap
app.navController.view.frame = CGRectOffset(app.navController.view.frame, 0.0, -20.0);
I had the same problem before. The code I used to add UINavigationBar to UIViewController:
UINavigationController *nc = [[UINavigationController alloc] initWithRootViewController:self];
[self.view addSubview:nc.view];
Solution:
Check the box "Wants Full Screen" with Attributes inspector of your UIViewController.
You can try to set the attribute Under Top Bars unchecked from Attributes section of UIViewController.
As we all know by now, the 20 pixel shift is to provide space for the Status bar on the top.
But infact, the view controllers coordinate system is kept in place and only the navigation bar frame is shifted down by 20 pixels. This makes the navigation bar to actually overlap the top 20 pixels of the view.
Logging the navigation bars frame origin, it will show (0.0, 20.0)
So the solution is to simply reposition the navigation bar's origin to (0.0, 0.0) in ViewWillAppear.
self.navigationController.navigationBar.frame = CGRectMake(0.0, 0.0, self.navigationController.navigationBar.frame.size.width, self.navigationController.navigationBar.frame.size.height);
Since you're adding advancedVC as a subview of self.view, it is being added inside the frame of self.view which I'm guessing is already compensating for the status bar.
You can probably easily fix this by adding this line:
nav.view.frame = self.view.frame;
Just above this line:
self.view = nav.view;
-
Other Thoughts
I'm not privy to your entire setup, but self.view may not be needed at all. Simply make your advancedVC instance the rootViewController of the UIWindow instance contained in your App Delegate.
The problem was resolved by fixing the way the navigation controller was inserted. Instead of inserting it into a view that had been put onto the tabbar controller, the navigaiton controller should have been put directly onto the navigation controller.
advancedSearchFormVC = [[AdvancedSearchFormVC alloc] initWithNibName:#"AdvancedSearchForm" bundle:nil];
UINavigationController *searchNavController = [[UINavigationController alloc] initWithRootViewController:advancedSearchFormVC];
This is just one controller that is on the tabbar controller, replacing the advancedSearchFormVC at the same time. Then this nav controller was added to the array of controllers that got put onto the tabbar controller.
Sorry for any trouble, but this was one of those problems I can look directly at and not see it. I should have seen this earlier, because I had another nav controller already on the tabbar controller, and it was set up the same way.
Thanks for your assistance.
The problem is that UINavigationController.view should be added to the top view.
Just find the top one and it will be working great.

Resources