4

I'd like to show and hide the statusBar and the navigationBar simultaneously using a slide effect.

This is how I tried:

[[UIApplication sharedApplication] setStatusBarHidden:hide withAnimation:UIStatusBarAnimationSlide];
[self.navigationController setNavigationBarHidden:hide animated:animated];

However, the duration of both animation is not the same. The status bar animation takes longer. I found no way how to specify the duration of either animation. Did I miss something obvious?

Ortwin Gentz
  • 52,648
  • 24
  • 135
  • 213

7 Answers7

5

ios-lizard's answer is almost what I wanted but the navigation bar re-appears when rotating the device unless hidden is set correctly. So this worked for me:

Hidding animating works/looks nice YEAH!!.

Showing animation is OK, (I wish I could make the status bar slide with the navigation bar but at least we don't see gaps anymore. :D )

- (void)toggleFullscreen {

    UINavigationBar *navBar = self.navigationController.navigationBar;
    CGRect statusBarFrame = [[UIApplication sharedApplication] statusBarFrame];
    float animationDuration;
    if(statusBarFrame.size.height > 20) { // in-call
        animationDuration = 0.5;
    } else { // normal status bar 
        animationDuration = 0.6;
    }

    _fullscreen = !_fullscreen;
    if (_fullscreen) { 
        // Change to fullscreen mode
        // Hide status bar and navigation bar
        [[UIApplication sharedApplication] setStatusBarHidden:YES
                                                withAnimation:UIStatusBarAnimationSlide];
        [UIView animateWithDuration:animationDuration animations:^{
            navBar.frame = CGRectMake(navBar.frame.origin.x,
                                  -navBar.frame.size.height,
                                  navBar.frame.size.width,
                                  navBar.frame.size.height);
        } completion:^(BOOL finished) {
            [self.navigationController setNavigationBarHidden:YES animated:NO];
        }];

    } else {
        // Change to regular mode
        // Show status bar and navigation bar
        [[UIApplication sharedApplication] setStatusBarHidden:NO
                                                withAnimation:UIStatusBarAnimationSlide];
        [UIView animateWithDuration:animationDuration animations:^{
             navBar.frame = CGRectMake(navBar.frame.origin.x,
                                       statusBarFrame.size.height,
                                       navBar.frame.size.width,
                                       navBar.frame.size.height);
        } completion:^(BOOL finished) {
            [self.navigationController setNavigationBarHidden:NO animated:NO];
        }];

    }

}
nacho4d
  • 43,720
  • 45
  • 157
  • 240
2

This is how I fixed this problem for my app.

    CGRect statusBarFrame = [[UIApplication sharedApplication] statusBarFrame];

    // delta is the amount by which the nav bar will be moved
    delta = statusBarFrame.size.height + self.navigationController.navigationBar.frame.size.height;

    if(statusBarFrame.size.height>20) { // in-call
        animationDuration = 0.5;
    }
    else { // normal status bar 
        animationDuration = 0.6;
    }

    // hide status bar
    [[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationSlide];

    // hide nav bar
    [UIView beginAnimations:nil context:nil];
    [UIView setAnimationDuration:animationDuration];

    self.navigationController.navigationBar.frame = CGRectOffset(self.navigationController.navigationBar.frame, 0.0, -delta);

    [UIView commitAnimations];
ios-lizard
  • 834
  • 1
  • 12
  • 19
1

Here's a more concise method that uses system constants for animation duration and also handles incoming calls.

Note that navigationBar is an outlet and statusBarHeight is an instance-variable float.

- (IBAction)toggleControls:(id)sender {
    BOOL isHidden = ! [UIApplication sharedApplication].statusBarHidden;
    if (isHidden)
        statusBarHeight = [UIApplication sharedApplication].statusBarFrame.size.height;
    [UIView animateWithDuration:[UIApplication sharedApplication].statusBarOrientationAnimationDuration animations:^{
        self.navigationBar.frame = CGRectMake(self.navigationBar.frame.origin.x,
                                              isHidden ? -self.navigationBar.frame.size.height : statusBarHeight,
                                              self.navigationBar.frame.size.width,
                                              self.navigationBar.frame.size.height);
    }];
    [[UIApplication sharedApplication] setStatusBarHidden:isHidden withAnimation:UIStatusBarAnimationSlide];
}
ioblomov
  • 37
  • 1
  • 8
1

nacho4d's answer is almost what I wanted .but,He changes navBar's frame before navBar is visible. So we can't see transition animation.It looks like navBar appear suddenly. What is more, when showing, statusBarFrame.size.height is equal to 0. Here is his code:

[[UIApplication sharedApplication] setStatusBarHidden:NO
                                            withAnimation:UIStatusBarAnimationSlide];
    [UIView animateWithDuration:animationDuration animations:^{
         navBar.frame = CGRectMake(navBar.frame.origin.x,
                                   statusBarFrame.size.height,
                                   navBar.frame.size.width,
                                   navBar.frame.size.height);
    } completion:^(BOOL finished) {
        [self.navigationController setNavigationBarHidden:NO animated:NO];
    }];

when Showing , we wish we could make the status bar slide with the navigation bar. here is my answer:

        UINavigationBar *navBar = self.navigationController.navigationBar;
        [[UIApplication sharedApplication] setStatusBarHidden:hidden withAnimation:UIStatusBarAnimationSlide];

        [UIView animateWithDuration:0.35 delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{
            // make navigationBar visual
            if (!hidden)
            {
                [self.navigationController setNavigationBarHidden:hidden animated:NO];
            }

            navBar.frame = CGRectMake(navBar.frame.origin.x,
                                      hidden ? -navBar.frame.size.height : 20,
                                      navBar.frame.size.width,
                                      navBar.frame.size.height);
        } completion:^(BOOL finished) {
            if (hidden)
            {
                [self.navigationController setNavigationBarHidden:hidden animated:NO];
            }
        }];
  1. when hidding ,and hidden equal to NO. we should change navBar's frame first,then make navBar hidden.
  2. when showing , and hidden equal to YES. we make navBar visual first,then change frame.
  3. we choose UIViewAnimationOptionCurveEaseOut, to make it looks better.
RY_ Zheng
  • 3,041
  • 29
  • 36
1

Clearly, there's no easy solution to do this right. Apple has to fix it.

Of course, one work-around is to use alpha fading as Ephraim suggests. If you're insisting on the sliding behavior, I found it best to just animate the navigation bar and hide/show the statusBar without any animation. This looks much better than sliding the status bar because the gap between the bars during the animation is quite noticeable.

Ortwin Gentz
  • 52,648
  • 24
  • 135
  • 213
0

You can use a instance variable to do this:

self.navigationController setNavigationBarHidden:hide animated:animated];
_shouldHideStatusBar = hide;

And implement the following function:

- (BOOL)prefersStatusBarHidden{
    return _shouldHideStatusBar;
}

The setNavigationBarHidden:animated function will automatically call prefersStatusBarHidden function. If it doesn't you can call it with the following UIViewController's method:

[self setNeedsStatusBarAppearanceUpdate];

And of course you can choose your status bar hiding animation style with:

- (UIStatusBarAnimation) preferredStatusBarUpdateAnimation {
    return UIStatusBarAnimationSlide;
}
The Windwaker
  • 1,054
  • 12
  • 24
0

This isn't much of an answer but it works. So what I did is:

// This method gets called by whatever action you want

- (void) toggleShowStatusNavBars:(id)sender {

    // Assuming you have a ivar called barsHidden

    [UIView beginAnimations:nil context:nil];
    [UIView setAnimationDuration:0.4]; // This is IMPORTANT, 0.4s

    self.navigationController.navigationBar.alpha = (barsHidden?1.0:0.0);  

    barsHidden = !barsHidden; 

    [UIView setAnimationDelegate:self];
    [UIView setAnimationWillStartSelector:@selector(setStatusBarHidden)];

    [UIView commitAnimations];
}

- (void) setStatusBarHidden {
    [[UIApplication sharedApplication] setStatusBarHidden:barsHidden animated:YES];
}

This will basically synchronize the start of the animation (since you are calling setStatusBarHidden at the start of the navigation bar animation. The key part is that the status bar animation seems to take 0.4 seconds.

This works for me but if you find a better way, do post here.

Pang
  • 9,564
  • 146
  • 81
  • 122
Ephraim
  • 2,234
  • 1
  • 21
  • 18
  • You're hiding the navBar using alpha fading. This is easier because in this case you can manipulate the navBar directly. In the sliding case I'm interested in that's not a viable approach since the navigationController also manipulates the content view frame. – Ortwin Gentz Oct 13 '10 at 22:00