Animation issue when hiding iPhone toolbar - iphone

I have a toolbar in my RootViewController and I then hide the toolbar in a SubViewController using the following code:
RootViewController
- (void)viewDidLoad {
...
[self.navigationController setToolbarHidden:FALSE animated:FALSE];
...
}
- (void)viewDidAppear:(BOOL)animated {
[self.navigationController setToolbarHidden:FALSE animated:TRUE];
[super viewDidAppear:animated];
}
SubViewController
- (void)viewDidLoad {
...
[self.navigationController setToolbarHidden:YES animated:YES];
[super viewDidLoad];
}
This all works as expected i.e. the toolbar will be hidden and unhidden using a nice vertical animation when moving from one view to another and back again.
However, there appears to be a nasty animation issue when moving from the RootViewController to the SubViewController. As the toolbar is being hidden, a white bar will appear where the toolbar was, and then quickly disappears across the screen from right to left.
Hopefully I've explained this well enough for you to understand.
Any ideas how to fix this?

Have you tried doing the animation in SubViewController's -viewWillAppear: method? You may have better luck there.

I have seen this problem a couple of times and I have found that putting the call to setToolbarHidden:animated: in the viewWillAppear: method does not always give a smooth animation with no white rectangle artifacts.
What does always work is to put the setToolbarHidden:animated: call in the viewDidAppear: method. This means that the toolbar hiding animation is triggered after the navigation controller has finished pushing the new view onto the stack, so no white rectangles. However, it also means that the whole animation is in two stages: the first animates the view in, the second hides the toolbar, so you have the appearance of a "delayed" toolbar hide. I acknowledge that this isn't always what you want.

TRY THIS
- (IBAction)hideTheToolBar:(id)sender{
//[toolBar setHidden:YES];
if (toolBar.hidden == NO)
{
[UIView animateWithDuration:0.25 delay:0.0
options:UIViewAnimationOptionCurveLinear | UIViewAnimationOptionAllowUserInteraction
animations:^(void)
{
toolBar.alpha = 0.0f;
}
completion:^(BOOL finished)
{
toolBar.hidden = YES;
}
];
}
}

You can (probably should) do this in the subview controller's designated initializer, e.g. initWithNibName:bundle:

I have found very useful to set the hidesBottomBarWhenPushed property in the init of your view controller.
For instance:
- (id)init
{
self = [super initWithStyle:UITableViewStyleGrouped];
if (self) {
// Custom initialization
self.navigationItem.rightBarButtonItem = self.editButtonItem;
self.hidesBottomBarWhenPushed = YES;
}
return self;
}
It hides those spurious toolbars that appear in the push and pop transitions. Also, it frees you from manually hiding the toolbar in the ViewWillAppear method or similar approaches.

Related

setNavigationBarHidden:YES doesn't work with the searchDisplayController

I'm using the following code to hide my navigationBar in the detailViewController(my second view),
and it works perfectly fine when I tap any of my object from the MasterViewController(my first view).
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self.navigationController setNavigationBarHidden:YES animated:animated];
}
However, when I filter the table list in the masterViewController using searchDisplayController
and tap any object from the result, the navigationBar in the detailView doesn't get hidden...
Do I have to do any extra work to hide the navigationBar if I use the searchDisplayController?
for Debug, I set the break point on the line of setNavigationBarHidden:YES, and even if
I go to the detailViewController via search result, the application hits the line..
you shuold put [self.navigationController setNavigationBarHidden:YES]; in viewWillLayoutSubviews function.like this:
- (void) viewWillLayoutSubviews
{
[super viewWillLayoutSubviews];
[self.navigationController setNavigationBarHidden:YES];
}
it works.
You should try this method:
In that controller, where you declared UISearchController *searchController, you should implement two methods (only for example):
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
// if you want to hide Navigation Bar when searchController will become active
_searchController.hidesNavigationBarDuringPresentation = YES;
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
_searchController.hidesNavigationBarDuringPresentation = NO;
}
The code above may have differences. Main point in hidesNavigationBarDuringPresentation property (iOS 8.0 and later). Try to play with it, and turn to hidesNavigationBarDuringPresentation = NO before pushing a new controller. After this manipulations I took profit: when pushed UIViewController, setter setNavigationBarHidden:YES become working
if you want to hide Navigation bar then, In your MainWindow xib uncheck "Shows Navigation Bar" attributes of Navigation Controller.
This Will hide the Navigation Bar in your Whole Project. If you want to Show Navigation Bar in any Controller set NavigationBar Hidden = NO in ViewDidLoad Method of that Controller.
you should hack search display controller in some way to hide its built in navigationBar.
here is the answer:
https://stackoverflow.com/a/6337037/1348121
This
- (void) viewWillLayoutSubviews
causes layout problems, so i used code below. Works fine for me.
-(void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
[self.navigationController setNavigationBarHidden:NO];
}

iPhone -- structuring code to avoid a race condition

I have an iPhone app that uses a UINavigationController, some table views, and iAd. At the top level, my navigation controller shows the navigation bar. At lower levels, it does not.
The problem I am having is that sometimes the frame of my top level UITableView goes below the bottom of the screen. The reason it happens is this:
my viewWillAppear method looks like this:
-(void) viewWillAppear:(BOOL)animated {
[self.navigationController setNavigationBarHidden:NO animated: animated]; // changing the last animated to NO does not help.
[super viewWillAppear:animated];
}
and my viewDidLoad method looks like this:
- (void)viewDidLoad {
[self.navigationController setNavigationBarHidden:NO animated: NO];
[super viewDidLoad];
[self createTableView];
ADBannerView *abv = [[ADBannerView alloc]initWithFrame: [self initialBannerViewFrame]];
abv.delegate=self;
[self.view addSubview:abv];
self.bannerView = abv;
[self moveBannerViewOffscreen];
[abv release];
}
Lastly, moveBannerViewOffscreen looks like this:
-(void) moveBannerViewOffscreen {
// moving it down and off
CGRect newBannerFrame = self.bannerView.frame;
CGFloat screenHeight = [[UIScreen mainScreen]bounds].size.height;
newBannerFrame.origin.y=screenHeight;
bannerView.frame = newBannerFrame;
CGRect newTableFrame = self.selectionTableView.frame;
newTableFrame.size.height = self.view.bounds.size.height;
self.selectionTableView.frame = newTableFrame;
}
When the view is loading, what happens is that even though I have called
[self.navigationController setNavigationBarHidden: NO animated: NO];
the the frame of my view is not immediately adjusted to account for the navigation bar. This is still true when moveBannerViewOffscreen executes. So the height of the table view is set to 480. When the navigation bar comes in, the result is that the bottom of the table view is below the screen, and the user can't select the last row.
I'm sure I could use an NSTimer to set up some kludge to fix this. But is there a clean way to organize my code so the problem doesn't come up in the first place?
Thanks
At first glance (without fully understanding your problem, I admit) I suspect that setting yourself as the navigation controller's delegate in order to take advantage of one of these methods would help with your timing:
navigationController:didShowViewController:animated:
navigationController:willShowViewController:animated:
perhaps not moving your banner until didShowViewController has been called.
(Apologies if I didn't follow your explanation.)

Transparent Background with a Modal UIViewController

I have a dilema, I want to present to the user a semi-transparent view.
I found out by experimenting that if I simply pushed the transparent view to the top of my NavigationController's stack, that it would not render the transparency level I wanted. So I decided to simply add the view as a subview of the current view at the top of the stack.
This solution works, the view below is still visible, and the View is 'semi-modal'. The problem is, if the parent view inherits from UITableViewController (as mine does), then the view I 'push' onto it, does not cover the navigation bar at the top.
I really don't want to get into a situation where I am forced to enable / disable controls on the navigation bar every time I push this view, so I was wondering, if anyone knew of any solutions that I could use so that the view I push onto the UITableViewController will actually 'push over' the navigation bar?
Funny, I was just doing the same thing yesterday. Unfortunately it seems to be impossible. Once the modal view controller is in place, the previous view becomes hidden.
See this previous question on the topic.
You can still use the view controller and NIB files you have set up - here's my sample code
- (void)showUpgrade {
[self.upgradeVC viewWillAppear:NO];
[self.view addSubview:self.upgradeVC.view];
[self.upgradeVC viewDidAppear:NO];
}
- (void)hideUpgrade {
[self.upgradeVC viewWillDisappear:NO];
[self.upgradeVC.view removeFromSuperview];
[self.upgradeVC viewDidDisappear:NO];
}
- (UpgradeViewController *)upgradeVC {
if (_upgradeVC == nil) {
_upgradeVC = [[UpgradeViewController alloc] initWithNibName:[NSString stringWithFormat:#"UpgradeView_%#", self.deviceType] bundle:nil];
_upgradeVC.delegate = self;
}
return _upgradeVC;
}
You will need to store a reference to the parent view controller in the modal view controller so that you can access the -hide method. I did this through a delegate.
It would also be easy to add some animation to -show and -hide if you want it to animate up from the bottom of the screen - I was just too lazy to do this.
iOS 8 added the UIModalPresentationOverFullScreen presentation style. Set this as the presented view controller’s modalPresentationStyle. For more advanced needs, look into creating a custom presentation controller.
There is now a way to achieve this using iOS7 custom transitions :
MyController * controller = [MyController new];
[controller setTransitioningDelegate:self.transitionController];
controller.modalPresentationStyle = UIModalPresentationCustom;
[self controller animated:YES completion:nil];
To create your custom transition, you need 2 things :
A TransitionDelegate object (implementing
<UIViewControllerTransitionDelegate>)
An "AnimatedTransitioning" object
(implementing <UIViewControllerAnimatedTransitioning>)
You can find more informations on custom transitions in this tutorial : http://www.doubleencore.com/2013/09/ios-7-custom-transitions/
Try this:
ViewController *vc = [[ViewController alloc] init];
[vc setModalPresentationStyle:UIModalPresentationOverCurrentContext];
[self presentViewController:vc animated:YES completion:nil];
Have you tried looping over the Modal View Controller's subviews and setting the background color to clear for every view? This is a DFS recursive function.
- (void)setBackgroundToClearForView:(UIView *)view {
if ([view subviews]) {
for (UIView *subView in [view subviews]) {
[self setBackgroundToClearForView:subView];
}
}
if ([view respondsToSelector:#selector(setBackgroundColor:)]) {
[view performSelector:#selector(setBackgroundColor:)
withObject:[UIColor clearColor]];
}
}
To use it call:
[self setBackgroundToClearForView:self.view];
in viewDidLoad.
This will do the trick.. Try this one.
// for clear color or you can easily adjust the alpha here
YourVC *vc=[[YourVC alloc]initWithNibName:#"YourVC" bundle:nil] ;
vc.view.backgroundColor = [UIColor clearColor];
self.modalPresentationStyle = UIModalPresentationCurrentContext;
[self presentViewController:vc animated:NO completion:nil];
So that the view will be full screen unlike UIModalPresentationFormSheet..

Keeping UINavigationController's navigationBar hidden after UISearchDisplayController selection

I have a UISearchDisplayController setup with a UITableViewController which is nested inside a UINavigationController. When a selection of a cell is made, UITableView's didSelectRowAtIndexPath method is triggered, which pushes a new view to the parent navigation controller. This new view should have the navigation bar hidden on entry.
[[self navigationController] setNavigationBarHidden:YES animated:NO];
I use this line in the didSelectRowAtIndexPath method to hide the navigation bar. This works fine when a row is selected not using the search controller, but is overridden when selecting a search result. It seems the UISearchDisplayController takes it in its right to un-hide the navigationBar sometime after the row is selected.
If I move the setNavigationBarHidden call into the target view's viewWillAppear method, results are similar. I can make it work by placing the hide call in viewDidAppear, but this makes for a very awkward transition effect which feels jumpy and out of place. I would like to make the navigationBar already hidden before the new view slides on to the screen.
Does anyone know where the unhiding of the navigationBar is occurring, and/or any way I can override this behaviour?
This may not be the most elegant solution, but I believe it does exactly what you'd want it to. I came across a similar problem, and my solution was to have a method which hides the navigation bar, which is called after a delay of 0 seconds as follows.
The method that is called is:
-(void) hideNavBar {
if (self.navigationController.navigationBar.hidden == NO)
{
[self.navigationController setNavigationBarHidden:YES animated:YES];
}
}
Then in the viewDidLoad method, I have the following:
[self performSelector:#selector(hideNavBar) withObject:nil afterDelay:0.0];
This works and removes the navigation bar in one instantaneous swoop. You can amend the delay time if you want the animation or for it to be removed after a delay. I tried [self hideNavBar] but that simply did not work, so sticking to what I have above.
Hope this helps, and if someone has a more elegant solution, I'm interested!
Ok, this bugged me for the a couple of hours, but I finally got it to work! The problem seems to be that the UISearchDisplayController keeps track of whether or not it has hid the navigation bar, and if it has, it restore it, after the view has been dismissed. That is why with many of the answers above you see the tail end of the animation of the bar hiding itself when the new view is pushed. However, by tricking the search display controller we can change this behavior.
First: Subclass The UISearchDisplayController
Following the answer on how to keep a navigation controller from hiding, found here, I altered the code, to keep the navigation bar hidden:
- (void)setActive:(BOOL)visible animated:(BOOL)animated
{
if(self.active == visible)
return;
[self.searchContentsController.navigationController setNavigationBarHidden:YES animated:YES];
[super setActive:visible animated:animated];
if (visible)
[self.searchBar becomeFirstResponder];
else{
[self.searchBar resignFirstResponder];
[self.searchContentsController.navigationController setNavigationBarHidden:NO animated:YES];
}
}
Note we hide the navbar before we call the super setActive function. This seems to keep the super class from trying to hide the nav bar and consequently, from trying to restore it ater item selection. Now when the controller becomes active, the bar will be hidden like normal. Also note that we restore the navigation bar when the searchBar resigns first responder. This will restore the bar if we cancel out of the controller.
Second: Hide Navigation Bar When Exiting
If we hide the navigation bar in the view will disappear, it will be hidden:
-(void)viewWillDisappear:(BOOL)animated{
[super viewWillDisappear:animated];
[self.navigationController setNavigationBarHidden:YES animated:animated];
}
Third: Hide Navigation Bar When Returning
The only problem now is that if we select a row from the filtered tableview, when we return, the navigation bar will be visible. To fix this we need to put a check in view will Appear:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
if(self.isFiltered){
[self.navigationController setNavigationBarHidden:YES animated:NO];
}
}
While this feels like a huge hack, it does the trick and I could see no better way of doing it.
Bumped into the same problem, managed to get it working smoothly with this ugly hack:
- (void) viewWillDisappear: (BOOL) animated
{
if (searchController_.active)
{
self.navigationController.navigationBar.barStyle = UIBarStyleBlackTranslucent;
self.navigationController.navigationBar.tintColor = nil;
}
[super viewWillDisappear: animated];
}
- (void) viewWillAppear: (BOOL) animated
{
if (searchController_.active)
{
self.navigationController.navigationBar.barStyle = UIBarStyleDefault;
}
[super viewWillAppear: animated];
}
I had the same problem: my view has the navigation bar hidden by default and here's the way to keep it hidden:
-(void) viewWillLayoutSubviews{
if (self.navigationController.navigationBar.hidden == NO)
{
[self.navigationController setNavigationBarHidden:YES animated:YES];
}
}
This way the navigation bar doesn't appear even after the search bar has been used.
- (void) viewWillDisappear: (BOOL) animated
{
// self.searchOn property tell if full screen search is enabled
//if (self.searchOn)
//{
[self.navigationController setNavigationBarHidden:NO animated:NO];
//}
[super viewWillDisappear: animated];
}
- (void) viewWillAppear: (BOOL) animated
{
//if (self.searchOn)
//{
[self.navigationController setNavigationBarHidden:YES animated:YES];
//}
[super viewWillAppear: animated];
}

How does presentModalViewController interact with nested UITabBarController and UINavigationController

I have a view that I want to take up the full screen, so I override the init method, and some of the view methods:
- (id) init {
if (self = [super init]) {
self.wantsFullScreenLayout = YES;
}
return self;
}
- (void) viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[[UIApplication sharedApplication] setStatusBarHidden:YES animated:YES];
}
- (void) viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[[UIApplication sharedApplication] setStatusBarHidden:NO animated:YES];
}
Now, from another screen, I want to display it as a modal view:
UIViewController *screen = [[MyScreen alloc] init];
[self presentModalViewController:screen];
[screen release];
All pretty standard stuff. When I want the full-screen view to go away, however, the previous view is shifted or stretched up by about 40 pixels.
Specificially, I have a UITabBarController with a UINavigationController inside, displaying a UITableViewController, which is the view that displays the subview, and also the view that gets shifted up. If the table is not in a navigation controller, everything works just fine, nothing gets shifted up at all. If I experiment with commenting out the wantsFullScreenLayout and setStatusBarHidden lines with no navigation bar, it sometimes shifts up just 20 pixels, or doesn't actually display on the full screen (but later it does without changing any code), or sometimes doesn't break at all (but I am not getting the full full screen with any of these)
What am I doing wrong?
Through some combination of Sean's suggestion and jumping up the responder chain, I've found a solution that works is what seems like all circumstances (so far).
First issue:
The Table View by itself does not display in a navigation controller, but may show up in one if being selected from the more view in the tab bar, and that's the case where displaying the modal view in full screen causes the table to underlap the navigation bar upon return.
Second issue:
When not displayed in a navigation controller, presenting the modal view does not take up the full screen (even though wantsFullScreenLayout is set to YES). When returning from this view, the view is shifted up by 20 pixels and you can see a gap between the bottom of the table and the top of the tab bar.
Solution:
- (void) viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self.navigationController setNavigationBarHidden:YES animated:NO];
[self.navigationController setNavigationBarHidden:NO animated:NO];
}
- (void) presentModalViewController:(UIViewController *)screen animated:(BOOL)animated {
UIResponder *responder = self;
while (responder && ![responder isKindOfClass:[UITabBarController class]]) {
responder = [responder nextResponder];
}
[(UIViewController *)responder presentModalViewController:screen animated:YES];
}
The toggling of the navigation bar's visibility forces the relayout. Overriding presentModalViewController actually calls presentModalViewController on the tab bar controller instead, which then causes it to show in the full screen. For some reason, self.tabBarController is nil when not in the more view controller, so I had to jump up the responder chain to find it.
Your UINavigationController will get called with the viewWillAppear before the modal view is dismissed. Have you tried calling [[UIApplication sharedApplication] setStatusBarHidden:NO animated:NO]; inside the controllers that can be visible post modal dismissal. I have run into tons of problems displaying modal views on top of UINavigationControllers when bounds change. It fights any layout changes and requires lots of resetting to previous states to get it behaving nicely. It might also not hurt to call [self.navigationController setNavigationBarHidden:NO animated:NO] as well to force layout.
If this works well it might serve you to create a simple baseclass that sets these in it's viewWillAppear and then just subclass it for all non modal view controllers.
If this doesn't work you might try placing a swap view at the top level that contains the tab bar controller and then you could remove the tab bar controller with a transition when you present your modal view. Yes this isn't technically modal but would still look nice and offer the same effect. At that time since the view controller is out of the view hierarchy it shouldn't get it's layout all munged.
I think this has to do with the timing of the presentModalViewController: call. As a test you could try adding sleep(3) before you call that method. If that fixes anything, or even if it doesn't i guess I would try moving the order of things around. maybe viewDidDisappear and viewDidAppear instead of 'Will'