How to hide/show status bar and navigation bar by

2019-07-18 05:16发布

问题:

I'm trying to hide and show the status bar and the navigation bar by fading them in and out at the same time like the Photos app in iOS 7. I've got the hiding part working, but I am having trouble with the showing part. The problem is that when I show the navigation bar, it is initially positioned as if the status bar is not there. At the end of fading in, it is positioned correctly (it is shifted down to make room for the status bar). How can I get the navigation bar to be positioned correctly throughout the animation?

Here's some code sketching out my current approach:

In some view controller, I control whether or not the status bar is hidden by overriding some UIViewController methods:

- (BOOL)prefersStatusBarHidden {
    return self.forcedStatusBarHidden;
}

- (UIStatusBarAnimation)preferredStatusBarUpdateAnimation {
    return UIStatusBarAnimationFade;
}

To hide the status bar and navigation bar at the same time, I do both in the same animation block:

void (^animations)() = ^() {
    theNavigationController.navigationBar.hidden = YES;

    someViewController.forcedStatusBarHidden = YES;
    [someViewController setNeedsStatusBarAppearanceUpdate];
};
[UIView transitionWithView:theNavigationController.navigationBar.superview
                  duration:0.5
                   options:UIViewAnimationOptionCurveEaseInOut
                           | UIViewAnimationOptionTransitionCrossDissolve
                           | UIViewAnimationOptionAllowAnimatedContent
                animations:animations
                completion:nil];

(Notice I use theNavigationController.navigationBar.hidden = YES instead of [theNavigationController setNavigationBarHidden:YES animated:YES] because I want the navigation bar to fade instead of sliding up. Also, for some reason, not including the UIViewAnimationOptionAllowAnimatedContent option does not make a difference.)

But if I do something similar to show the status bar and navigation bar together, I get the problem I described earlier.

void (^animations)() = ^() {
    someViewController.forcedStatusBarHidden = NO;
    [someViewController setNeedsStatusBarAppearanceUpdate];

    theNavigationController.navigationBar.hidden = NO;
};
[UIView transitionWithView:theNavigationController.navigationBar.superview
                  duration:0.5
                   options:UIViewAnimationOptionCurveEaseInOut 
                           | UIViewAnimationOptionTransitionCrossDissolve
                           | UIViewAnimationOptionAllowAnimatedContent
                animations:animations
                completion:nil];

The closest I've gotten to getting it to look right is to show the bars in sequence instead of in the same animation block:

someViewController.forcedStatusBarHidden = NO;
[someViewController setNeedsStatusBarAppearanceUpdate];

void (^animations)() = ^() {
    theNavigationController.navigationBar.hidden = NO;
};
[UIView transitionWithView:theNavigationController.navigationBar.superview
                  duration:0.5
                   options:UIViewAnimationOptionCurveEaseInOut 
                           | UIViewAnimationOptionTransitionCrossDissolve
                           | UIViewAnimationOptionAllowAnimatedContent
                animations:animations
                completion:nil];

But now the bars do not fade in together. (EDIT: If I put the first two lines in their own animation block to force the animation duration of the status bar fading in, I get the original problem with the navigation bar.) How do I fix this?

Note: I'm using a custom background image for the navigation bar. If I just use the default frosted/blurred background for the navigation bar, another problem is that the background is invisible when it is supposed to be fading in and suddenly appears at the end of the fade-in animation. If I can get this working for the frosted/blurred background as well, that would be great.

Another note: Just in case it makes a difference, the navigation controller is presented with theNavigationController.modalPresentationStyle = UIModalPresentationCustom.

回答1:

I figured out the answer to my own question. The trick is to disable the animation just for setting the frame, bounds, and center for the navigation bar during the fade-in animation. This is done by subclassing UINavigationBar and conditionally using [UIView performWithoutAnimation...] whenever the frame, bounds, and center are set. For example:

- (void)setFrame:(CGRect)frame
{
    if (self.shouldAnimateDimensions) {
        [super setFrame:frame];
    }
    else {
        [UIView performWithoutAnimation:^{
            [super setFrame:frame];
        }];
    }
}

The fading-in code then becomes:

void (^animations)() = ^() {
    // myNavigationBar is theNavigationController.navigationBar
    myNavigationBar.shouldAnimateDimensions = NO;

    someViewController.forcedStatusBarHidden = NO;
    [someViewController setNeedsStatusBarAppearanceUpdate];

    myNavigationBar.shouldAnimateDimensions = YES;

    theNavigationController.navigationBar.hidden = NO;
};
[UIView transitionWithView:theNavigationController.navigationBar.superview
                  duration:0.5
                   options:UIViewAnimationOptionCurveEaseInOut 
                           | UIViewAnimationOptionTransitionCrossDissolve
                           | UIViewAnimationOptionAllowAnimatedContent
                animations:animations
                completion:nil];