I have a uinavigationcontroller where i push a UIPageViewController with UIViewController1, UIViewcontroller2, ....!
I want to have the Navigationbar on UIViewController1, but not on the other UIViewController.
So on the first UIViewController1 i would have:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self.navigationController setNavigationBarHidden:NO animated:NO];
}
and on the other UIViewController2, UIViewController3, ....
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self.navigationController YES animated:NO];
}
Everything works fine when i move forward! But if i am sliding backward after moving from UIViewController3 to UIViewController2, suddenly the navigationBar already apperas on UIViewController2. I guess UIViewController1 is already appearing while i am looking on UIViewController2 and suddenly it gets the notification to show the UINavigationBar.
Putting the Commands into UINavigationBarDidAppear does not solve the problem :-(
Can Somebody help me out?
Found the answer. Actually everything works fine. I was just fooled by my Spaghetti Code i guess.
I didn't notice i already was hiding the navigationbar in the loadview method and then again in the viewWillAppear method. After removing the one in loadView its working perfectly!
Related
Assuming we are having three screens that are pushed after one another like this
A->B->C
And screen A is originally in a tabBar.
The navigation bar should be hidden in screen C and visible in all of the rest.To do this am doing the following
-(void) viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
[self.navigationController setNavigationBarHidden: YES animated:NO];
}
The viewWillAppear gets called in all of the cases but hiding or showing the navigationBar is not necessarily gets reflected on the UI.
For example if used the back button the navigation bar appears in both A & B but if tapped the tabBarButton ,which causes the app to jump to screen A directly even from screen C, screen A will be missing the navigationBar.
I've check the self.navigatioController and it's initialized and has a value.
I've tried also to set the NavigationBarHidden property in the viewDidAppear but with no luck.
Any help on that issue? what may cause such a weird scenario?
Edit: Answer
I discovered the issue.
Screen C is a complex screen of a lot of containers.In one of the containers i was changing the navigationBar state and that affect everything else in the app and caused the weird behaviour and made me unable to control the state by myself.
Sorry for the trouble.
In both viewController A and viewController B, use this:
-(void) viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
[self.navigationController setNavigationBarHidden: NO animated:NO];
}
In viewController c, use this(as mentioned in question):
-(void) viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
[self.navigationController setNavigationBarHidden: YES animated:NO];
}
I discovered the issue.Screen C is a complex screen of a lot of containers.
in one of the containers i was changing the navigationBar state and that affect everything else in the app and caused the weird beahviour .
Try the following :-
[self.navigationController setNavigationBarHidden: YES animated:YES];
in place of :-
[self.navigationController setNavigationBarHidden: YES animated:NO];
I knew its a duplicate. But still having an issue and even when tried with possibilities didn't work. Hence posting the same to reach a solution. Hope to get help from you guys.
The initial is embedded inside UINavigationController. For the initial (the landing view) the navigation bar must be hidden. The other views when called from the landing view - must show the navigation bar.
I'm handling the hide & show of navbar in the landing view by overriding the methods of the view as follows:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
// Hiding the navigationbar hidden for the first page
[[self navigationController] setNavigationBarHidden:YES animated:YES];
}
// Even tried animated:NO & animated:animated
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
// Showing the navigationbar hidden for the first page
[[self navigationController] setNavigationBarHidden:NO animated:YES];
}
While the app loads initially, the nav bar is in hidden state (as expected & working fine). When coming back to the landing view from the child view controller, the nav bar gets hidden after some seconds - the landing view gets loaded on to the ui screen.
I also tried using the navigationcontroller delegate method in landing view: navigationController: willShowViewController: animated:. But unable to reach the solution that i need.
Hence i provided the navigationcontroller delegate in one of my childviewcontroller and checked whether the childcontroller when popped is not in viewcontrollers of the navigationcontroller using if condition. When yes, then i provided the hide option of the navigationbar. but also failed to have the solution.
During surfed, there was a solution to handle with viewanimation. I tried and that too failed.
Again surfed, the solution provided across is to handle the similar issue with viewwillappear & viewwilldisappear. I'm blinked since the way i'm doing is similar to the proposed way. Even then unable to reach a solution.
FYI.. I'm using Xcode 6.3 and deployment target is 6.0 onwards. I'm using storyboard to manage views.
Please help me sort the issue... App loads is hiding the nav bar in landing page. But when landing page is loaded back from a child view then the nav bar gets hidden only after the landing page loaded on to the ui. I do need to get hidden of the nav bar as like when app loads, when the child view pops and the landing view gets loaded on the top of the controller.
If you want to hide navigation bar in the second view then don't try to manage in viewWillAppear and viewWillDisappear because I have faced a lot of problems by trying like that and it also effected the constraints. Just use delegate for navigation controller in appDelegate it is working fine for me.
self.navigationController.delegate = self;
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
if ([viewController isKindOfClass:[LoginViewController class]])
{
[self.navigationController setNavigationBarHidden:YES animated:animated];
} else {
[self.navigationController setNavigationBarHidden:NO animated:animated];
}
}
Use this method:
[navigationController setNavigationBarHidden:YES];
So, if you are in some view controller:
[self.navigationController setNavigationBarHidden:YES];
More clarifications:
UINavigationController has a property navigationBarHidden, that allows you to hide/show navigation bar for whole nav controller.
Let's look at the next hierarchy:
--UINavigationController
----UIViewController1
----UIViewController2
----UIViewController3
Each of three UIViewController will have nav bar since they are in UINavigationController. For example, you want to hide bar into the second (actually it doesn't matter in which one), then write into UIViewController2:
-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self.navigationController setNavigationBarHidden:YES]; //it hides
}
-(void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[self.navigationController setNavigationBarHidden:NO]; // it shows
}
Overwrite the delegate method in your custom UINavigationController class:
-(void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
[self setNavigationBarHidden:NO];
if ([viewController isKindOfClass:[SomeViewController class]])
{
[self setNavigationBarHidden:YES];
}
}
One advantage for putting it in your UINavigationController class is that you don't clutter your UIViewController class with code
Tested and works.
UPDATE
Create a UINavigationController subclass: f.e. MyNavigationController
In AppDelegate.h:
#import "MyNavigationController.h"
#property (nonatomic) MyNavigationController *navigationController;
Then initialise it in AppDelegate.m:
-(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
//Probably some more code here
self.navigationController = [[MyNavigationController alloc] initWithRootViewController:yourRootViewController];
self.window.rootViewController = self.navigationController;
self.window.backgroundColor = [UIColor blackColor];
[self.window makeKeyAndVisible];
return YES;
}
Then overwrite the delegate method in your custom UINavigationController class
I have little to no experience with storyboards so not really sure how to setup a custom UINavigationController, but this is how I do it in code.
Here's another SO post how to create a custom UINavigationController to use with storyboards.
You can see on the gif below that on the first scroll of UITableView cell's content moves a tiny bit. You can barely see it, margins become 1 pixel wider.I've never encountered this before. It seems like there's some layout issue before the first scroll and it resolves itself after the fact. There's no warning in XCode, these custom cells are pretty straightforward, with no layout code overrides.
I don't know where to start, how do I catch this glitch?
UPDATE. I've implemented an obvious workaround for now:
- (void)scrollTableToFixGlitch {
[self.tableView setContentOffset:CGPointMake(0, 1)];
[self.tableView setContentOffset:CGPointMake(0, -1)];
}
- (void)viewDidLoad {
[super viewDidLoad];
[self scrollTableToFixGlitch];
}
Still looking into the problem. I've tried generic UITableViewCells, nothing changed. Seems like it's the problem with View Controller's root view or tableview layout, and not with the table cells.
UPDATE 2.
I ruled out all the animations out of the question, problem lies somewhere in a different region. The glitch is easy to recreate on a much simplified project. My Tab Bar controller is based on MHCustomTabBarController with custom segues and some other additions. Here's what you do to recreate a glitch. Setup a project where your initial VC is embedded in Navigation Controller. The next controller either MHCustomTabBarController or a subclass is pushed to the navigation stack. First visible VC in tab bar is generic Table View Controller. That's it. Glitch appears only if tab bar controller is pushed in navigation stack.
Here's some code that I think matters from tab bar controller:
-(void) viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
if (self.childViewControllers.count < 1) {
[self performSegueWithIdentifier:#"viewController1" sender:[self.buttons objectAtIndex:0]];
}
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if (![segue isKindOfClass:[MHTabBarSegue class]]) {
[super prepareForSegue:segue sender:sender];
return;
}
self.oldViewController = self.destinationViewController;
//if view controller isn't already contained in the viewControllers-Dictionary
if (![self.viewControllersByIdentifier objectForKey:segue.identifier]) {
[self.viewControllersByIdentifier setObject:segue.destinationViewController forKey:segue.identifier];
}
[self.buttons setValue:#NO forKeyPath:#"selected"];
[sender setSelected:YES];
self.selectedIndex = [self.buttons indexOfObject:sender];
self.destinationIdentifier = segue.identifier;
self.destinationViewController = [self.viewControllersByIdentifier objectForKey:self.destinationIdentifier];
[[NSNotificationCenter defaultCenter] postNotificationName:MHCustomTabBarControllerViewControllerChangedNotification object:nil];
}
And a custom segue code:
#implementation MHTabBarSegue
- (void) perform {
MHCustomTabBarController *tabBarViewController = (MHCustomTabBarController *)self.sourceViewController;
UIViewController *destinationViewController = (UIViewController *) tabBarViewController.destinationViewController;
//remove old viewController
if (tabBarViewController.oldViewController) {
[tabBarViewController.oldViewController willMoveToParentViewController:nil];
[tabBarViewController.oldViewController.view removeFromSuperview];
[tabBarViewController.oldViewController removeFromParentViewController];
}
destinationViewController.view.frame = tabBarViewController.container.bounds;
[tabBarViewController addChildViewController:destinationViewController];
[tabBarViewController.container addSubview:destinationViewController.view];
[destinationViewController didMoveToParentViewController:tabBarViewController];
}
#end
UPDATE 3
During my research I've found that - viewWillAppear is not called the first time when child controller appears. But it's called in all subsequent times.
Maybe the scrollviews contentSize is wider than your scrollView's frame(width specifically in this case) causing scrolling for both directions.
You can either try to decrease the contentSize width to the scrollView's width and
self.scrollView.alwaysBounceHorizontal = NO;
If this doesn't work, the solution would be to disable horizontal scrolling programatically by the help of the UIScrollView delegate
self.scrollView.delegate = self;
[self.scrollView setShowsHorizontalScrollIndicator:NO];
//for the below UIScrollView delegate function to work do the necessary step in the bottom of my answer.
-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
if (scrollView.contentOffset.x > 0)
scrollView.contentOffset = CGPointMake(0, scrollView.contentOffset.y);
}
And in your .h file you should change the interface line to below by adding UIScrollViewDelegate
#interface ViewController : UIViewController <UIScrollViewDelegate>
You most probably know this delegate part very well but for others it might be needed:D
Original answer
Ah, I've finally found the origin of this behaviour. I was almost sure this is happening due to some of the preparation methods are not called properly. As I stated in the update 3 I've found that -viewWillAppear method is not called in TableViewController when my TabBarController is pushed to the navigation stacked. I've found a ton of coverage of this matter on SO, it's a very well known issue apparently.
I've added a simple fix just to check if I'm right in my Custom Segue:
//added this line
[destinationViewController viewWillAppear:YES];
[tabBarViewController.container addSubview:destinationViewController.view];
And it works like a charm, no flickering! Now I have to figure out a more suitable place for this call, since explicit calls to methods like this can break a lot of stuff.
Probably the best place is in -navigationController:willShowViewController:animated: method of UINavigationControllerDelegate.
Anyway, problem solved. Hope it will help someone with the same issue.
UPDATE Actually, I was not completely correct on that. -viewWillAppear is called on my tab bar controller when it's pushed to navigation stack. It's not being translated to TableViewController. So there's no need to access NavigationControllerDelegate. Simple fix to a custom segue is enough.
Currently I have a UIViewController with a couple of buttons that when pressed move to a UITableViewController. Each button loads a specific array of data to the UITableViewController by identifying the segue of the specific button and displaying the corresponding data.
This works fine as is.
However I wish to add an embedded UINavigationController so I can navigate through the UITableViewController and and corresponding views while still being able to 'press back' to the initial UIViewController.
Firstly, where am I meant to put this. I tried over the tableViewController and 2 things happen -
a) If the segues still go to the UITableViewController, there is no navigation displayed.
b) If I move the segues to go to the UINavigationController, none of my arrays show in the tableViewController, but I do have navigation.
Where do I link my segues or where do I embed the UINavigationController so this works?
(I haven't put any code as I don't think this will involve it, but if it does just let me know and I will add).
Not sure if I'm missing something but, it's not working. I want to be able to still utilize the buttons I already have and not use the buttons on the navigation from the UIViewController to the UITableViewController.
For Hiding the Navigation on root
- (void)viewWillAppear:(BOOL)animated {
[self.navigationController setNavigationBarHidden:YES animated:animated];
[super viewWillAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated {
[self.navigationController setNavigationBarHidden:NO animated:animated];
[super viewWillDisappear:animated];
}
The ViewController which has 2 buttons can be made as rootViewController to the NavigationController. The NavigationController can be added to the window.
Window -> NavigationController -> UIViewController (as rootView)
Hope this helps.
Select the root view controller->editor->embed in->navigation controller
I use following method to disable the Navigation bar throughout the app:
[navcontroller setNavigationBarHidden:YES animated:YES];
But is it possible to disable it only for one ViewController?
Certainly. Whenever you enter a viewcontroller, you can enable or disable for that viewcontroller (just call [[self navigationController] setNavigationBarHidden:YES animated:YES] during viewWillAppear
The nicest solution I have found is to do the following in the first view controller.
- (void)viewWillAppear:(BOOL)animated
{
[self.navigationController setNavigationBarHidden:YES animated:animated];
[super viewWillAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated
{
[self.navigationController setNavigationBarHidden:NO animated:animated];
[super viewWillDisappear:animated];
}
This will cause the navigation bar to animate in from the left (together with the next view) when you push the next UIViewController on the stack, and animate away to the left (together with the old view), when you press the back button on the UINavigationBar.
Please note also that these are not delegate methods, you are overriding UIViewController's implementation of these methods, and according to the documentation you must call the super's implementation somewhere in your implementation.
Hopefully this will resolve your problem.