20

iOS Developers will surely knows about the issue about status bar and the famous "slide/hamburger/drawer". The issue is well explained here: http://uxmag.com/articles/adapting-ui-to-ios-7-the-side-menu

I'm using MMDrawerController library and it has a nice hack that lets us to create a dummy status bar just above the container view controller. Unfortunately this doesn't work really good. What's the news? The news is that I stumbled upon an app (Tinder) that perfectly solve this mind blowing issue. I've created a gif that perfectly shows what Tinder does.

enter image description here

You need to wait a few seconds for seeing the gif because there's a bug in it and I don't know how to get rid of. Just wait one/two seconds and you will able to see the gif correctly.


Anyway, what Tinder does? When the user taps on the top left menu button and begin to swipe right the status bar fades out neatly. And when the view is revert to the original position the status bar will show up again.

I am both happy and a bit sad for this because this means that a way must be to do it but I really don't know how to implement it (perhaps hacking MMDrawerController). Any help will be so much appreciated.


IMPORTANT

Please pay attention to the fact that the method setStatusBarHidden: will completely hide the status bar, this means that the entire view is with a height -20px. This is obviously not the solution because as you can see from the gif the view is not stretched.

Fred Collins
  • 5,004
  • 14
  • 62
  • 111
  • Are you using autolayout? – Alladinian Jan 08 '14 at 16:44
  • 1
    Couldn't it be that the app is designed to appear the same regardless of the status bar behind hidden or not? For instance by using constraints to the top of the contentView instead of using the top layout guide? That would fix the -20px issue, but requires adapting the app obviously. – Taum Jan 08 '14 at 16:47
  • Taum the problem was that if I'd use the method `setStatusBarHidden:` the animation will hide the status bar and so there's an ugly animation while the view controller adapting to the new height. – Fred Collins Jan 09 '14 at 01:42
  • @FredCollins what happen my answer it doesn't work. – codercat Jan 09 '14 at 12:46

5 Answers5

21

Your main problem is with MMDrawerController. If you'll digg into it you'll find a lot of methods statusbar related such as setShowsStatusBarBackgroundView setStatusBarViewBackgroundColor and more. Something in their code pushes the view up when the statusbar is hidden.

Alternatively you can use another drawer controller or use custom code.

Here's a simple way how to accomplishe this:

enter image description here

ViewControllerA:

-(BOOL)prefersStatusBarHidden
{
    return _hidden;
}
- (void)statusHide
{
    [UIView animateWithDuration:0.4 animations:^() {[self setNeedsStatusBarAppearanceUpdate];
    }completion:^(BOOL finished){}];
}

ViewControllerB: (Container in ViewControllerA)

- (IBAction)move:(UIButton *)sender
{
    parent = (ViewController*)self.parentViewController;
    parent.hidden = !parent.hidden;
    CGRect frame = parent.blueContainer.frame;
    if(parent.hidden)
    {
        frame.origin.x = 150;
    }
    else
    {
        frame.origin.x = 0;
    }

    [UIView animateWithDuration:1 animations:^() {parent.blueContainer.frame = frame;}completion:^(BOOL finished){}];
    [parent statusHide];
}

For iOS 6 compatieblty use:

[[UIApplication sharedApplication] setStatusBarHidden:_hidden withAnimation:UIStatusBarAnimationFade];

The table view and other subviews will stay in their location and won't be pushed up.

Edit:

Adding a NavigationBar:

UINavigationController will alter the height of its UINavigationBar to either 44 points or 64 points, depending on a rather strange and undocumented set of constraints. If the UINavigationController detects that the top of its view’s frame is visually contiguous with its UIWindow’s top, then it draws its navigation bar with a height of 64 points. If its view’s top is not contiguous with the UIWindow’s top (even if off by only one point), then it draws its navigation bar in the “traditional” way with a height of 44 points. This logic is performed by UINavigationController even if it is several children down inside the view controller hierarchy of your application. There is no way to prevent this behavior.

Taken from here

You could very simply subclass UINavigationController and create your own navbar to avoid this annoyness.

Segev
  • 19,035
  • 12
  • 80
  • 152
  • Ohhh very nice Sha! Do you mind to share your simple test project? :) Anyway I didn't thought that the problem was ``MMDrawerViewController`. I should look into its code and try to fix this issue! – Fred Collins Jan 09 '14 at 01:47
  • I've just tried do the same thing you showed and it works ONLY if the center view controller is not a navigation controller. I think this because a navigation controller has a navbar attached to the top layout guide and try to fix to it. You center/right view controller is a navigation controller? The light blue view is a navbar? – Fred Collins Jan 09 '14 at 02:16
  • Yup, if I try to add a simple `UINavigationBar` it works but if the controller is a navigation controller with its own navigation bar doesn't work because the bar try to stay attached to the top. Hmm – Fred Collins Jan 09 '14 at 02:18
  • @FredCollins The above answers the question fully. I don't think Tinder is using the common navbar. Infect, I almost positive they are not even using a `UINavigationController`. It's a single view app with one or two model view controllers. Anyway, I've updated my answer with some more interesting info. – Segev Jan 09 '14 at 07:05
  • Thanks Sha. A last question. I've carefully read the page you linked but I've not understood a thing: when we try to hide the status bar in a navigation controller the navigation bar should again be fixed to the `UIWindow`'s top, it's again visually contiguous, not? If yes, why the navigation controller resize its navigation bar? – Fred Collins Jan 10 '14 at 00:50
  • @FredCollins These are undocumented set of constraints. For the "why" part you'll need to email apple. – Segev Jan 11 '14 at 08:56
5

i don't know if it will sove your problem but i got almost the same effect using the SWRevealViewController project. In the appDelegate I've set the delegate method from this class to do this:

- (void)revealController:(SWRevealViewController *)revealController willMoveToPosition:(FrontViewPosition)position {
#ifdef DEBUG
    NSArray *teste = @[@"FrontViewPositionLeftSideMostRemoved",@"FrontViewPositionLeftSideMost",@"FrontViewPositionLeftSide",@"FrontViewPositionLeft",@"FrontViewPositionRight",@"FrontViewPositionRightMost",@"FrontViewPositionRightMostRemoved"];
    NSLog(@"%@ %d", teste[position], position);
#endif
    if (position == FrontViewPositionRight)
        [[UIApplication sharedApplication] setStatusBarHidden:YES  withAnimation:UIStatusBarAnimationFade];
    UINavigationController *frontViewController = (id)revealController.frontViewController;
    frontViewController.navigationBar.centerY += (position == FrontViewPositionRight) ?  20 : 0; //  20 == statusbar heihgt
}

- (void)revealController:(SWRevealViewController *)revealController didMoveToPosition:(FrontViewPosition)position {
    if (position == FrontViewPositionLeft)
        [[UIApplication sharedApplication] setStatusBarHidden:NO  withAnimation:UIStatusBarAnimationFade];
}

centerY is a category in the UIView which sets the center.y without dealing the boring part of setting frame variables.

Bruno Gama
  • 120
  • 1
  • 8
3

Here is how you should do that in iOS 7:

@implementation ViewController
{
    BOOL _hideStatusBar;
}

-(UIStatusBarStyle)preferredStatusBarStyle
{
    return UIStatusBarStyleDefault;
}

-(UIStatusBarAnimation)preferredStatusBarUpdateAnimation
{
    return UIStatusBarAnimationFade;
}

-(BOOL)prefersStatusBarHidden
{
    return _hideStatusBar;
}

-(void)setStatusBarHidden:(BOOL)hidden
{
    [UIView animateWithDuration:1.0 animations:^{
        _hideStatusBar = hidden;
        [self setNeedsStatusBarAppearanceUpdate];
    }];
}

@end
Felix
  • 35,354
  • 13
  • 96
  • 143
  • 1
    this will work but only this does not solve the navigationBar and child controllers sliding up if you have set the translucent = NO in the navigationbar. – Bruno Gama Feb 27 '14 at 05:40
1

Check out the method setStatusBarHidden:withAnimation: on UIApplication. It will allow you to show or hide the status bar and the animation can be none, fade, or slide. You just need to add a call to hide the bar and one to show the bar at the correct times and decide if you like the fade as you illustrated or if the slide works better for you.

https://developer.apple.com/library/ios/DOCUMENTATION/UIKit/Reference/UIApplication_Class/Reference/Reference.html#//apple_ref/occ/instm/UIApplication/setStatusBarHidden:withAnimation:

theMikeSwan
  • 4,739
  • 2
  • 31
  • 44
  • Then add in a UIView that matches the navigation bar but has the same frame as the status bar. You will likely have to experiment with the timing a bit to get the effect you want. – theMikeSwan Jan 06 '14 at 21:53
  • How? If you closely look at Tinder it simply hides the status bar without messing with new views. You can see that by looking at how the status bar fades out when the side menu animation is working. – Fred Collins Jan 08 '14 at 00:42
1

You can used -setStatusBarHidden:withAnimation: if you adjust your views frame in -viewDidAppear:, then you will not see any stretch. Note that autolayout is disabled.

-(void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    CGRect frame = self.view.frame;
    // adjust root view frame 
    frame.origin.y -= 20;
    frame.size.height += 20;

    [self.view setFrame:frame];

    // adjust subviews y position
    for (UIView *subview in [self.view subviews])
    {
        CGRect frame = subview.frame;
        frame.origin.y += 20;
        [subview setFrame:frame];
    }
}

- (IBAction)sliderChanged:(id)sender
{
    UISlider *s = (UISlider *)sender;
    if (s.value > .5)
    {
        UIApplication *app = [UIApplication sharedApplication];
        if (![app isStatusBarHidden])
            [app setStatusBarHidden:YES withAnimation:UIStatusBarAnimationFade];
    }
    else
    {
        UIApplication *app = [UIApplication sharedApplication];
        if ([app isStatusBarHidden])
            [app setStatusBarHidden:NO withAnimation:UIStatusBarAnimationFade];
    }
}

enter image description here

Emmanuel
  • 2,897
  • 1
  • 14
  • 15