How to hide status bar with animation in iOS 7? - ios

Since iOS 7 rolled out, I can't show or hide status bar with animation just like in iOS 6.
For now I use NSTimer to control it when to hide.
here is my code:
- (void)hideStatusBar{
_isStatusBarHidden=YES;
[self performSelector:#selector(setNeedsStatusBarAppearanceUpdate)];
}
- (void)showStatusBar{
_isStatusBarHidden=NO;
[self performSelector:#selector(setNeedsStatusBarAppearanceUpdate)];
}
//===================
_controlVisibilityTimer = [[NSTimer scheduledTimerWithTimeInterval:4 target:self selector:#selector(hideStatusBar:) userInfo:nil repeats:NO] retain];
But unfortunately the way of status bar hiding seems a little bit rough, not fading away. Is someone out there has a solution to this ?
Update
I solved the hiding issue, using #hahaha solution. I just need a view to be the background of the status bar, here is my code.
AppDelegate* appDelegate = (AppDelegate*)[[UIApplication sharedApplication]delegate];
self.StatusBarOrange = [[UIView alloc] initWithFrame:CGRectMake(0, 0, appDelegate.window.frame.size.width, 20)];
[self.StatusBarOrange setBackgroundColor:[UIColor orangeColor]];
[appDelegate.window.rootViewController.view addSubview:self.StatusBarOrange];
and now everything works perfectly!

You need to call
[UIViewController setNeedsStatusBarAppearanceUpdate];
from within an animation block as in the following example:
#implementation SomeViewController {
BOOL _statusBarHidden;
}
- (BOOL)prefersStatusBarHidden {
return _statusBarHidden;
}
- (void)showStatusBar:(BOOL)show {
[UIView animateWithDuration:0.3 animations:^{
_statusBarHidden = !show;
[self setNeedsStatusBarAppearanceUpdate];
}];
}
#end

Related

how to hide systems status bar and show alert in status bar for few sec by animation in iOS 9

I want to show alert in status bar for small duration with animation and hide systems status bar for that duration
I have referred this but enable to hide system's status bar for that particular time,failed to add animation
Here is my code
NSString *status=#"welcome..";
UIView *notificationView= [JDStatusBarNotification showWithStatus:(NSString *)status styleName:JDStatusBarStyleDark];
CGRect statusBarFrame = [[UIApplication sharedApplication] statusBarFrame];
notificationView.frame=statusBarFrame;
[self.view addSubview:notificationView];
UIView *dismissNotificationView=[JDStatusBarNotification showWithStatus:(NSString *)status
dismissAfter:(NSTimeInterval)5.0f styleName:JDStatusBarStyleDark];
[self.view addSubview:dismissNotificationView]; `
also tried this but it moves another window then shows and turns back
here is the code used
MTStatusBarOverlay *overlay = [MTStatusBarOverlay sharedInstance];
overlay.animation = MTStatusBarOverlayAnimationFallDown; // MTStatusBarOverlayAnimationShrink
overlay.detailViewMode = MTDetailViewModeHistory; // enable automatic history-tracking and show in detail-view
overlay.delegate = self;
overlay.progress = 0.0;
[overlay postImmediateFinishMessage:#"welcome" duration:2.0 animated:YES];
overlay.progress = 1.0;
please help..thanks in advance
I'm not sure when you want the alert to appear, so I will assume just after loading the view. I didn't use the two frameworks, but tested it with a generic red UIView and it worked with the code below:
#import "ViewController.h"
#interface ViewController () {
BOOL animating;
CGRect aimFrame;
}
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
animating = YES; //To set if we are showing the alert or not
aimFrame = [[UIApplication sharedApplication] statusBarFrame];
[self setNeedsStatusBarAppearanceUpdate];
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
UIView *notificationView = [[UIView alloc] init];
[notificationView setFrame:aimFrame];
[notificationView setBackgroundColor:[UIColor redColor]];
[self.view addSubview:notificationView];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[notificationView removeFromSuperview];
animating = NO;
[self setNeedsStatusBarAppearanceUpdate];
});
}
- (BOOL)prefersStatusBarHidden {
// Toggle based upon if we are showing the alert
if (animating) {
return YES;
} else {
return NO;
}
}
#end
I hope this helps, let me know if it still doesn't work or if I miss understood something :)
Try my code to complete hide the status bar:
-(BOOL)prefersStatusBarHidden{
return YES;
}

Show splash screen each time app becomes active

I want the splash screen to be displayed each time app becomes active. I have created a function showSplash which I call in applicationDidBecomeActive:
-(void)showSplash
{
UIImageView *splashScreen = [[UIImageView alloc] initWithImage:[UIImage imageNamed: #"Default.png"]];
[self.window.rootViewController.view addSubview: splashScreen];
[self.window makeKeyAndVisible];
NSLog(#"begin splash");
[UIView animateWithDuration: 4.2
delay: 0.5
options: UIViewAnimationOptionCurveEaseOut
animations: ^{
splashScreen.alpha = 0.0;
}
completion: ^ (BOOL finished) {
[splashScreen removeFromSuperview];
NSLog(#"end splash");
}
];
}
This is how I call this function :
- (void)applicationDidBecomeActive:(UIApplication *)application {
[self showSplash];
}
But no splash screen appears. Please correct me.
If you want the application to have a fresh start every time you get back to it, you could also disable background execution, as stated in the Apple Documentation (last section titled "Opting Out of Background Execution"):
If you do not want your app to run in the background at all, you can
explicitly opt out of background by adding the
UIApplicationExitsOnSuspend key (with the value YES) to your app’s
Info.plist file.
after adding the splash view - bring it to front
change
UIImageView *splashScreen = [[UIImageView alloc] initWithImage:[UIImage imageNamed: #"Default.png"]];
[self.window.rootViewController.view addSubview: splashScreen];
[self.window makeKeyAndVisible];
to
UIImageView *splashScreen = [[UIImageView alloc] initWithImage:[UIImage imageNamed: #"Default.png"]];
[self.window addSubview: splashScreen];
[self.window bringSubviewToFront: splashScreen]; //!
[self.window makeKeyAndVisible];
Please, ensure your window root view controller main view is the top most view in your App in the moment you want to show splash...
Check frame of splashScreen (UIImageView). Set its frame to bounds of your root view controller.
You can try to hunt the top view controller with a recursion like this:
- (UIViewController *)findTopViewController {
return [self topViewControllerFrom:self.window.rootViewController];
}
- (UIViewController *)topViewControllerFrom:(UIViewController *)vc {
if (vc.navigationController.visibleViewController != nil) {
return [self topViewControllerFrom:vc.navigationController.visibleViewController];
}
if (vc.tabBarController.selectedViewController != nil) {
return [self topViewControllerFrom:vc.tabBarController.selectedViewController];
}
return vc;
}
Now calling [self findTopViewController] should hopefully return the currently visible/top VC of your app, and you can do:
[[self findTopViewController].view addSubview:splashScreen];
...
you can do something like:
#import "AppDelegate.h"
#define kSplashScreen (UIScreen.mainScreen.bounds.size.height == 568) ? #"Default-568h" \
: (UIScreen.mainScreen.bounds.size.height == 667) ? #"Default-667" \
: (UIScreen.mainScreen.bounds.size.height == 736) ? #"Default-Portrait" \
: #"Default"
#interface AppDelegate ()
#property (nonatomic) UIWindow *splashWindow;
#property (nonatomic) UIWindow *keyWindow;
#property (nonatomic, getter=isSplashConfigured) BOOL splashConfigured;
#property (nonatomic, getter=isShowingSplash) BOOL showingSplash;
#end
#implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[self animateSplash];
return YES;
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
[self showOrHideSplash];
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
[self showOrHideSplash];
}
- (void)showOrHideSplash
{
[self.splashWindow setHidden:[self isShowingSplash]];
if ([self isShowingSplash])
{
[self.keyWindow makeKeyAndVisible];
}
else
{
[self.splashWindow makeKeyAndVisible];
[self animateSplash];
}
self.showingSplash = !self.showingSplash;
}
- (void)animateSplash
{
if ([self isSplashConfigured])
{
[self.window.rootViewController.view addSubview:self.splashWindow];
[self.window makeKeyAndVisible];
}
else
{
self.keyWindow = [[UIApplication sharedApplication] keyWindow];
self.splashWindow = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.splashWindow.rootViewController = [[UIViewController alloc] init];
self.splashWindow.rootViewController.view.frame = self.splashWindow.bounds;
UIImageView *splashImage = [[UIImageView alloc] initWithImage:[UIImage imageNamed:kSplashScreen]];
splashImage.frame = self.splashWindow.bounds;
[self.splashWindow.rootViewController.view addSubview:splashImage];
[self.splashWindow makeKeyAndVisible];
[self.splashWindow setHidden:YES];
self.splashConfigured = YES;
}
NSLog(#"begin splash");
__weak AppDelegate* weakSelf = self;
[UIView animateWithDuration:4.2 delay:0.5 options:UIViewAnimationOptionCurveEaseOut animations: ^{
weakSelf.splashWindow.alpha = 1.0;
weakSelf.splashWindow.alpha = 0.0;
} completion: ^(BOOL finished) {
[weakSelf.splashWindow removeFromSuperview];
NSLog(#"end splash");
}];
}
#end
This is a bit cumbersome solution, but it works fine. The idea is add new UIWindow on top of all controllers
- (void)showSplash {
id <UIApplicationDelegate> appDelegate = [[UIApplication sharedApplication] delegate];
UIWindow *window = [[UIWindow alloc] initWithFrame:[appDelegate window].frame];
UIViewController *splashController = [UIViewController new];
UIImageView *imgView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"Default.png"]];
splashController.view = imgView;
window.rootViewController = splashController;
window.windowLevel = UIWindowLevelAlert;
window.hidden = NO;
[window makeKeyAndVisible];
[UIView animateWithDuration:4.2 delay:0.5 options:UIViewAnimationOptionCurveEaseOut animations:^{
window.alpha = 0.0;
} completion:^(BOOL finished) {
window.hidden = YES;
window.rootViewController = nil;
[[appDelegate window] makeKeyAndVisible];
[[appDelegate window] setNeedsDisplay];
}];
}
Please find below a solution for this problem.
Split the splash screen code into two methods; showSplash and hideSplash.
Call showSplash in the method applicationWillResignActive. In the method, create and add the imageView to rootViewController; set it's alpha to 0.0f and then animate it to alpha 1.0f. This is to make sure that the user doesn't see the imageView when the app is going to background.
Now call hideSplash in applicationDidBecomeActive. In hideSplash remove the imageView with animation. See the code below;
-(void)showSplash
{
_splashScreen = [[UIImageView alloc] initWithImage:[UIImage imageNamed: #"Default"]];
[self.window.rootViewController.view addSubview:_splashScreen];
_splashScreen.alpha = 0.0f;
[UIView animateWithDuration:0.3f animations:^{
_splashScreen.alpha = 1.0f;
}];
}
- (void)hideSplash
{
[UIView animateWithDuration: 4.2
delay: 0.5
options: UIViewAnimationOptionCurveEaseOut
animations: ^{
_splashScreen.alpha = 0.0;
}
completion: ^ (BOOL finished) {
[_splashScreen removeFromSuperview];
NSLog(#"end splash");
}
];
}
- (void)applicationWillResignActive:(UIApplication *)application {
[self showSplash];
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
[self hideSplash];
}
I hope this helps! :-)
Add
splashScreen.frame = self.window.rootViewController.view.frame;
below
UIImageView *splashScreen = [[UIImageView alloc] initWithImage:[UIImage imageNamed: #"alertBg.png"]];
example
-(void)showSplash
{
UIImageView *splashScreen = [[UIImageView alloc] initWithImage:[UIImage imageNamed: #"Default.png"]];
splashScreen.frame = self.window.rootViewController.view.frame;
[self.window.rootViewController.view addSubview: splashScreen];
[self.window makeKeyAndVisible];
NSLog(#"begin splash");
[UIView animateWithDuration: 4.2
delay: 0.5
options: UIViewAnimationOptionCurveEaseOut
animations: ^{
splashScreen.alpha = 0.0;
}
completion: ^ (BOOL finished) {
[splashScreen removeFromSuperview];
NSLog(#"end splash");
}
];
}
The best way to achieve this is to add the image in AppDelegate's window.
In the below code statusView is one of the view, which consist of some image. So add it to your AppDelegate's window as a subView
[[[[UIApplication sharedApplication] delegate] window] addSubview:statusView];
Now whenever you want it to appear for some amount of time, you show this view, but same time bring it to front.
-(void)showStatusView{
[UIView animateWithDuration:0.5 animations:^{
[[[[UIApplication sharedApplication] delegate] window] bringSubviewToFront:statusView];
statusView.alpha = 1;
}];
}
Better Call the above method on the AppDelegate's method didBecomeActive.
Also, show the splash screen as soon as application is going to resign active. This way, iOS will take the snapshot of the screen, which will be shown for fraction of seconds when the app is about to become active.
- (void)applicationDidEnterBackground:(UIApplication *)application {
[self showStatusView];
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
[self showStatusView];
}
Once the app is active, you can show the actual splash screen for some duration & then your usual app state.
First create your launch screen or image in didFinishLaunchingWithOptions function.
After this write your code like this-
Root view controller is not always the presented view controller. This can cause your splash screen to be hidden under bunch of other view controllers. Try using something like (swift syntex):
func getTopViewController() -> UIViewController?{
if var topController = UIApplication.sharedApplication().keyWindow?.rootViewController{
while ((topController.presentedViewController) != nil) {
topController = topController.presentedViewController!
}
return topController
}
return nil
}
And add your splash view to the top view controller
launch your application normally every-time, call your method in applicationBecomeActive, and add you image view on Appdelegate.window and remove it after some time by timer.
[self.window addSubview: splashScreen];
You should use the following line:
[self.window addSubview: splashScreen];
instead of
[self.window.rootViewController.view addSubview: splashScreen];
You can reuse your launchScreen to show up instead of showing imageview. This comes handdy when you have complex launchscreen instead of single image as a splash screen.
func applicationWillResignActive(_ application: UIApplication) {
guard let window = UIApplication.shared.windows.last,
let launchScreen = UIStoryboard(name: "LaunchScreen", bundle: nil).instantiateInitialViewController(),
let launchView = launchScreen.view else {return}
launchView.tag = 8888
launchView.frame = window.bounds
launchView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
window.addSubview(launchView)
window.makeKeyAndVisible()
}
func applicationDidBecomeActive(_ application: UIApplication) {
guard let window = UIApplication.shared.windows.last,
let view = window.viewWithTag(8888) else {return}
view.removeFromSuperview()
}

Unable to hide status bar on iOS 6,7 when presenting viewcontroller

The following is my custom VC presentation code:
-(void)presentViewController:(UIViewController*)vc
{
UIWindow *w = [[[UIApplication sharedApplication] delegate] window];
UIViewController *parentController = (TabBarViewController *)[w rootViewController];
[parentController addChildViewController:vc];
if ([vc respondsToSelector:#selector(beginAppearanceTransition:animated:)]) // iOS 6
{
[vc beginAppearanceTransition:YES animated:YES];
}
UIView *toView = vc.view;
[parentController.view addSubview:toView];
toView.frame = parentController.view.bounds;
CGAffineTransform tr = CGAffineTransformScale(self.view.transform, 1.0f, 1.0f);
toView.transform = CGAffineTransformScale(self.view.transform, 0.01f, 0.01f);;
CGPoint oldCenter = toView.center;
toView.center = ((RootViewControllerEx*)vc).cellCenter;
[UIView animateWithDuration:4.5 animations:^{
toView.transform = tr;
toView.center = oldCenter;
} completion:^(BOOL finished) {
[vc didMoveToParentViewController:parentController];
if ([vc respondsToSelector:#selector(endAppearanceTransition)]) // iOS 6
{
[vc endAppearanceTransition];
}
}];
}
It works fine, however, in presented VC I am hiding status bar:
- (BOOL)prefersStatusBarHidden {
return YES;
}
When I present my VC using built-in presentViewController:animated:completion:, status bar in presented VC is hidden. But with my code on iOS 7 status bar is not hidden at all, on iOS 6 it is even more strange - status bar is hidden, but my view size is shorter from top by the size of status bar. So I can see a black gap from top on iOS 6. What should I do to properly hide status bar when using custom VC presentation?
you should try this in your viewDidLoad for differencing the IOS 6/7 status bar problem
if ([self respondsToSelector:#selector(setNeedsStatusBarAppearanceUpdate)])
{
//IOS 7 - Status Bar Hidden
[self prefersStatusBarHidden];
[self performSelector:#selector(setNeedsStatusBarAppearanceUpdate)];
self.statusBarHidden = YES;
}
else
{
// iOS 6 - Status Bar shown
[[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationSlide];
self.statusBarHidden = NO;
}
and an method for hiding status Bar
- (BOOL)prefersStatusBarHidden{
return YES;}
and also add an property for status Bar
#property BOOL statusBarHidden;
then make sure that your view bounds to the screen size and fits correctly
I think this solves your problem :)
Try this
in view did load
[UIApplication sharedApplication].statusBarHidden = YES;
and set value in plist like
set this in project summary
and this in your interface builder

MPMoviePlayerViewController hides StatusBar and destroys Frame after watching

i created a MPMoviePlayerViewController in my UIView (not in my UIVIewController!) like this:
self.playButton = [[UIButton alloc] initWithFrame:CGRectMake(100, 70, 125, 100)];
[self.playButton setBackgroundImage:[UIImage imageNamed:#"video_play.png"] forState:UIControlStateNormal];
[self.playButton addTarget:self action:#selector(buttonPressed:) forControlEvents: UIControlEventTouchUpInside];
self.playerViewController = [[MPMoviePlayerViewController alloc] initWithContentURL:videoURL];
self.playerViewController.moviePlayer.fullscreen = NO;
self.playerViewController.view.frame = CGRectMake(0, 0, 320, 200);
[self.playerViewController.moviePlayer prepareToPlay];
self.playerViewController.moviePlayer.shouldAutoplay = NO;
self.playerViewController.view.backgroundColor = [UIColor yellowColor];
[self addSubview:self.playerViewController.view];
[self.playerViewController.view addSubview:self.playButton];
}
- (void)buttonPressed:(id)sender{
(NSLog(#"Click"));
[self.playerViewController.moviePlayer setFullscreen:YES animated:YES];
[self.playerViewController.moviePlayer play];
}
As you can see i added a Button on the videoView, beacause this part should only be a Preview and when the user clicks on the Button the MPMoviePlayerViewController should animate to fullscreen and start playing, and when the Video is finished it should go back to the Preview View. Everything works so far, but i have two Problems:
First Problem:
Everytime i open the View my StatusBar becomes hidden and it looks like this:
So i set in my viewWillAppear and viewDidAppear of my UIViewController:
[[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationNone];
This works, but now the statusbar gets hidden and immediately appears again, that looks ugly, any chance to solve this Problem?
Second Problem:
When i click on the Custom Button the Video gets fullscreen and everything works correct! But when i press the DONE Button of the Video everything goes back to the Preview Screen and it looks like this: The StatusBar is hidden, the navigationbar is also broken and there is a lot of black Space above the Video, whats the Problem here?
Ok i found a solution for this Problem, its a little bit hacky but i found no other solution. In my ViewController i do:
- (void)viewDidLoad {
[super viewDidLoad];
float delay = 0.1;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, delay * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
[UIApplication sharedApplication].statusBarHidden = NO;
});
and for the case User taps back Button i do:
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[UIApplication sharedApplication].statusBarHidden = NO;
}

How to slide in/out statusBar and navigationBar simultaneously?

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?
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];
}];
}
}
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];
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.
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];
}
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];
}
}];
when hidding ,and hidden equal to NO. we should change navBar's frame first,then make navBar hidden.
when showing , and hidden equal to YES. we make navBar visual first,then change frame.
we choose UIViewAnimationOptionCurveEaseOut, to make it looks better.
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;
}
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.

Resources