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
Related
This is my question
CONTEXT
I have a ViewController which I have an effect where the Navigation Bar gets transparent when the user go down in the scroll, and the Navigation Bar gets normal when the user go up in scroll view. This effect I did with the UIScrollViewDelegate's methods. This is the code:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGFloat offset = scrollView.contentOffset.y;
if (scrollView.contentOffset.y < 0){
scrollView.bounces = false;
}else{
scrollView.bounces = true;
}
CGFloat currentAlpha = (offset / 310);
if (currentAlpha < 0) {
self.navigationController.navigationBar.translucent = YES;
self.navigationController.navigationBar.alpha = 1;
self.navigationController.titleNavBar.alpha = 0; //This property I made in an UINavigationController extension
} else {
self.navigationController.navigationBar.translucent = YES;
self.navigationController.navigationBar.alpha = 1;
self.navigationController.titleNavBar.alpha = currentAlpha; //This property I made in an UINavigationController extension
[self.navigationController.navigationBar setBackgroundColor:[UIColor colorWithRed:0.0f/0.0f green:136.0f/255.0 blue:206.00f/255.0f alpha:currentAlpha]];
}
}
With the previous code I got the effect, but I have a problem: I can't add it to the status bar because self.navigationController.navigationBar.translucent is set on YES. So, in the iPhones, the status bar shows transparent and in the iPhone X shows bigger this transparence than another iPhones (see the image).
Anyone know how can I do this transparent effect with Navigation Bar and the Statsus Bar?
You need to change statusBar UIView color with NavigationBar color.
Create AppDelegate SharedInastance :
+ (AppDelegate *)sharedAppDelegate {
return (AppDelegate *)[UIApplication sharedApplication].delegate;
}
Add below code in AppDelegate to get status bar view:
- (UIView *)statusBarView {
return [[[UIApplication sharedApplication] valueForKey:#"statusBarWindow"] valueForKey:#"statusBar"];
}
Add below code when you want to change status bar color:
[[AppDelegate sharedAppDelegate] statusBarView].backgroundColor = [UIColor redColor];
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;
}
I have a UINavigationController as the root view controller of my UIWindow on iOS 7 and iOS 8. From one of its view controllers, I present a fullscreen modal view controller with a cross-dissolve presentation style. This modal view controller should be able to rotate to all orientations, and it works fine.
The problem is when the device is held in a landscape orientation and the modal view controller is dismissed. The view controller which presented the modal only supports portrait orientation, and I've confirmed that UIInterfaceOrientationMaskPortrait is returned to -application:supportedInterfaceOrientationsForWindow:. -shouldAutorotate returns YES, as well. However, the orientation of the presenting view controller, after dismissing the modal, remains landscape. How can I force it to remain in portrait orientation while allowing the modal to take the orientation of the device? My code follows:
App delegate:
- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
UINavigationController *navigationController = (UINavigationController *)self.deckController.centerController;
NSArray *viewControllers = [navigationController viewControllers];
UIViewController *top = [viewControllers lastObject];
if (top && [top presentedViewController]) {
UIViewController *presented = [top presentedViewController];
if ([presented respondsToSelector:#selector(isDismissing)] && ![(id)presented isDismissing]) {
top = presented;
}
}
return [top supportedInterfaceOrientations];
}
return (UIInterfaceOrientationMaskLandscapeLeft|UIInterfaceOrientationMaskLandscapeRight);
}
Presenting view controller:
- (BOOL)shouldAutorotate {
return YES;
}
- (NSUInteger)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskPortrait;
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
return UIInterfaceOrientationPortrait;
}
Modal view controller:
- (BOOL)shouldAutorotate
{
return YES;
}
- (NSUInteger)supportedInterfaceOrientations
{
return (UIInterfaceOrientationMaskLandscape|UIInterfaceOrientationMaskLandscapeLeft|UIInterfaceOrientationMaskPortrait);
}
If the modal controller was in landscape orientation before dismissal, the presenting ViewController may not return to the origin orientation (portrait). The problem is because the AppDelegate supportedInterfaceOrientationsForWindow method is called before the controller is actually dismissed and the presented controller check still returns Landscape mask.
Set a flag to indicate whether the (modal) presented view controller will be displayed or not.
- (void)awakeFromNib // or where you instantiate your ViewController from
{
[super awakeFromNib];
self.presented = YES;
}
- (IBAction)exitAction:(id)sender // where you dismiss the modal
{
self.presented = NO;
[self dismissViewControllerAnimated:NO completion:nil];
}
And in the modal presented ViewController set the orientation according to the flag: When the modal ViewController is presented - return Landscape. When it is dismissed then return portrait
- (NSUInteger)supportedInterfaceOrientations
{
if ([self isPresented]) {
return UIInterfaceOrientationMaskLandscape;
} else {
return UIInterfaceOrientationMaskPortrait;
}
}
Last step - from your AppDelegate call the modal presented ViewController for its orientation. I am just checking the currently presented ViewController and call the supportedInterfaceOrientations on it
- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
NSUInteger orientationMask = UIInterfaceOrientationMaskPortrait;
UIViewController *currentVC = self.window.rootViewController.presentedViewController; // gets the presented VC
orientationMask = [currentVC supportedInterfaceOrientations];
return orientationMask;
}
For more info check this link
This solution is for iOS 8+.
Problem description
Application key window have UINavigationController's subclass as its rootViewController.
This NC subclass prohibits some of the interface orientations.
Some View Controller (VC1) in the NC stack is presenting another View Controller (VC2) modally and fullscreen.
This presented VC2 allows more interface orientations than NC do.
User rotates device to orientation that is prohibited by NC, but allowed by presented VC2.
User dismisses the presented VC2.
View of VC1 has incorrect frame.
Setup and illustration
UINavigationController's subclass:
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}
- (BOOL)shouldAutorotate
{
return YES;
}
VC1 initial appearance and UI view stack:
Presenting VC2 (QLPreviewController in that example) from VC1:
QLPreviewController *pc = [[QLPreviewController alloc] init];
pc.dataSource = self;
pc.delegate = self;
pc.modalPresentationStyle = UIModalPresentationFullScreen;
[self.navigationController presentViewController:pc animated:YES completion:nil];
VC2 is presented and device rotated to landscape:
VC2 dismissed, device is back in portrait mode, but NC stack remains in landscape:
Cause
Apple documentation states:
When you present a view controller using the presentViewController:animated:completion: method, UIKit always manages the presentation process. Part of that process involves creating the presentation controller that is appropriate for the given presentation style.
Apparently there is a bug in handling UINavigationController stack.
Solution
This bug can be bypassed by providing our own transitioning delegate.
BTTransitioningDelegate.h
#import <UIKit/UIKit.h>
#interface BTTransitioningDelegate : NSObject <UIViewControllerTransitioningDelegate>
#end
BTTransitioningDelegate.m
#import "BTTransitioningDelegate.h"
static NSTimeInterval kDuration = 0.5;
// This class handles presentation phase.
#interface BTPresentedAC : NSObject <UIViewControllerAnimatedTransitioning>
#end
#implementation BTPresentedAC
- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext
{
return kDuration;
}
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)context
{
// presented VC
UIViewController *toVC = [context viewControllerForKey:UITransitionContextToViewControllerKey];
// presented controller ought to be fullscreen
CGRect frame = [[[UIApplication sharedApplication] keyWindow] bounds];
// we will slide view of the presended VC from the bottom of the screen,
// so here we set the initial frame
toVC.view.frame = CGRectMake(frame.origin.x, frame.origin.y + frame.size.height, frame.size.width, frame.size.height);
// [context containerView] acts as the superview for the views involved in the transition
[[context containerView] addSubview:toVC.view];
UIViewAnimationOptions options = (UIViewAnimationOptionCurveEaseOut);
[UIView animateWithDuration:kDuration delay:0 options:options animations:^{
// slide view to position
toVC.view.frame = frame;
} completion:^(BOOL finished) {
// required to notify the system that the transition animation is done
[context completeTransition:finished];
}];
}
#end
// This class handles dismission phase.
#interface BTDismissedAC : NSObject <UIViewControllerAnimatedTransitioning>
#end
#implementation BTDismissedAC
- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext
{
return kDuration;
}
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)context
{
// presented VC
UIViewController *fromVC = [context viewControllerForKey:UITransitionContextFromViewControllerKey];
// presenting VC
UIViewController *toVC = [context viewControllerForKey:UITransitionContextToViewControllerKey];
// inserting presenting VC's view under presented VC's view
toVC.view.frame = [[[UIApplication sharedApplication] keyWindow] bounds];
[[context containerView] insertSubview:toVC.view belowSubview:fromVC.view];
// current frame and transform of presented VC
CGRect frame = fromVC.view.frame;
CGAffineTransform transform = fromVC.view.transform;
// determine current presented VC's view rotation and assemble
// target frame to provide naturally-looking dismissal animation
if (transform.b == -1) {
// -pi/2
frame = CGRectMake(frame.origin.x + frame.size.width, frame.origin.y, frame.size.width, frame.size.height);
} else if (transform.b == 1) {
// pi/2
frame = CGRectMake(frame.origin.x - frame.size.width, frame.origin.y, frame.size.width, frame.size.height);
} else if (transform.a == -1) {
// pi
frame = CGRectMake(frame.origin.x, frame.origin.y - frame.size.height, frame.size.width, frame.size.height);
} else {
// 0
frame = CGRectMake(frame.origin.x, frame.origin.y + frame.size.height, frame.size.width, frame.size.height);
}
UIViewAnimationOptions options = (UIViewAnimationOptionCurveEaseOut);
[UIView animateWithDuration:kDuration delay:0 options:options animations:^{
// slide view off-screen
fromVC.view.frame = frame;
} completion:^(BOOL finished) {
// required to notify the system that the transition animation is done
[context completeTransition:finished];
}];
}
#end
#implementation BTTransitioningDelegate
- (id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source
{
return [[BTPresentedAC alloc] init];
}
- (id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed
{
return [[BTDismissedAC alloc] init];
}
#end
Import that transitioning delegate in presenting VC:
#import "BTTransitioningDelegate.h"
Store a strong reference to an instance:
#property (nonatomic, strong) BTTransitioningDelegate *transitioningDelegate;
Instantiate in -viewDidLoad:
self.transitioningDelegate = [[BTTransitioningDelegate alloc] init];
Call when appropriate:
QLPreviewController *pc = [[QLPreviewController alloc] init];
pc.dataSource = self;
pc.delegate = self;
pc.transitioningDelegate = self.transitioningDelegate;
pc.modalPresentationStyle = UIModalPresentationFullScreen;
[self.navigationController presentViewController:pc animated:YES completion:nil];
I ended up subclassing the UINavigationController and overriding its rotation methods. The following solution works on iOS 7, but I believe there is a bug in iOS 8 beta 5 that causes the presenting view controller's view to shrink to half the screen-height after dismissing the modal in landscape orientation.
UINavigationController subclass:
- (BOOL)shouldAutorotate
{
return NO;
}
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return UIInterfaceOrientationPortrait;
}
I have a controller embedded in navigation controller. Let's say that i have a button that repositions self.navigationController.navigationBar a bit. Then i do presentViewControllerAnimated with any controller (doesn't matter if it's nav or not) and after dismissing it navigation bar returns to it's original position (actually it is at its original position at dismiss animation start). In iOS 6 and earlier the bar would not be repositioned automatically. Any idea how can i prevent this repositioning in iOS 7?
OK, so I finally got it right.
First of all - Apple does not want us to change position of UINavigationBar. Therefore you should avoid it at all cost. In my case i got an app to fix which moved UINavigationBar to show slide-out menu. The proper solution to slide-out menu problem is to put UINavigationController inside - then you can slide whole UINavigationController with its content (whatever it is) and everything works fine. For some reason UINavigationController was outside in this app. So, i had to resort to a hack. Do not use this method if you have ANY option not to use it. It's a hack, it might break in further iOS versions and Apple would certainly not appreciate it.
First, explore new transitioning system in iOS7: http://www.doubleencore.com/2013/09/ios-7-custom-transitions/
Then, replace:
[self presentViewController:navigationController animated:YES completion:nil];
with
if([UIApplication iOS7]) /* or any other custom iOS7 detection method you implement */
{ /* we simulate old transition with nav bar slided out */
navigationController.transitioningDelegate = [OMModalTransitionDelegate new];
}
[self presentViewController:navigationController animated:YES completion:nil];
So, we need a transition delegate to simulate standard behaviour and do the trick as well.
#import "OMModalTransitionDelegate.h"
#import "OMAnimatedTransitioning.h"
#implementation OMModalTransitionDelegate
- (id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source
{
OMAnimatedTransitioning *transitioning = [OMAnimatedTransitioning new];
return transitioning;
}
- (id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed
{
OMAnimatedTransitioning *transitioning = [OMAnimatedTransitioning new];
transitioning.reverse = YES;
return transitioning;
}
#end
And now the actual animation manager (you have to implement sharedBar in a category on UINavigationBar yourself):
static NSTimeInterval const DEAnimatedTransitionDuration = 0.4f;
static NSTimeInterval const DEAnimatedTransitionMarcoDuration = 0.15f;
#implementation OMAnimatedTransitioning
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIView *container = [transitionContext containerView];
UIView *superView = [UINavigationBar sharedBar].superview;
CGRect barFrame = [UINavigationBar sharedBar].frame;
if(self.reverse)
{ /* Trick - first, remove the bar from it's superview before animation starts */
[[UINavigationBar sharedBar] removeFromSuperview];
}
CGRect oldFrame = container.bounds;
if (self.reverse)
{
[container insertSubview:toViewController.view belowSubview:fromViewController.view];
}
else
{
toViewController.view.frame = oldFrame;
toViewController.view.transform = CGAffineTransformMakeTranslation(0, CGRectGetHeight(oldFrame));
[container addSubview:toViewController.view];
}
[UIView animateKeyframesWithDuration:DEAnimatedTransitionDuration delay:0 options:0 animations:^
{
if (self.reverse)
{
fromViewController.view.transform = CGAffineTransformMakeTranslation(0, CGRectGetHeight(oldFrame));
double delayInSeconds = 0.01; /* Trick - after an imperceivable delay - add it back - now it is immune to whatever Apple put there to move it */
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void)
{
[UINavigationBar sharedBar].frame = barFrame;
[superView addSubview:[UINavigationBar sharedBar]];
});
}
else
{
toViewController.view.transform = CGAffineTransformIdentity;
}
} completion:^(BOOL finished) {
[transitionContext completeTransition:finished];
}];
}
- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext
{
return DEAnimatedTransitionDuration;
}
#end
In your custom navigation controller, add
- (void)viewWillLayoutSubviews {
//do your navigation bar layout
}
hope this can help you. Remind, above method only be supported ios >= 5.0.
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.