Hiding status bar after view has loaded leaves black bar - iphone

I'm running into a bit of a weird problem when hiding the status bar after the view had loaded. If I add the following method in the ViewDidLoad method, the status bar is completely removed from the view:
[[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationSlide];
However, if I call this method in an IBAction or another method, the status bar still slides away but leaves a black bar the same height as itself behind.
I've thought about shifting the entire view up by 20px but is this really a fix? I don't want to just overlap a black bar incase the status bar height changes in future OS upgrades.

Hard-coding any number is always counter to future proofing. Your concerns are correct. There is a bit of a trick to properly handling the hiding of the statusBar. But all the needed information is available.
For example the UIApplication singleton has a property named statusBarFrame which is precisely what it sounds like, a CGRect of the statusBar's frame. The cool thing is that once you have called setStatusBarHidden:withAnimation: that property will give you the new frame, even before the animation completes. So really you are simply left with some basic math to adjust the view's frame.
In short your gut feeling is correct; always compute things live.
I've had good success with a category method like this. (Even when toggling in-call status bar in simulator(Command - T)):
#implementation UIApplication (nj_SmartStatusBar)
// Always designate your custom methods by prefix.
-(void)nj_setStatusBarHidden:(BOOL)hidden withAnimation:(UIStatusBarAnimation)animation{
UIWindow *window = [self.windows objectAtIndex:0];
UIViewController *rootViewController = window.rootViewController;
UIView *view = rootViewController.view;
// slight optimization to avoid unnecassary calls.
BOOL isHiddenNow = self.statusBarHidden;
if (hidden == isHiddenNow) return;
// Hide/Unhide the status bar
[self setStatusBarHidden:hidden withAnimation:animation];
// Get statusBar's frame
CGRect statusBarFrame = self.statusBarFrame;
// Establish a baseline frame.
CGRect newViewFrame = window.bounds;
// Check if statusBar's frame is worth dodging.
if (!CGRectEqualToRect(statusBarFrame, CGRectZero)){
UIInterfaceOrientation currentOrientation = rootViewController.interfaceOrientation;
if (UIInterfaceOrientationIsPortrait(currentOrientation)){
// If portrait we need to shrink height
newViewFrame.size.height -= statusBarFrame.size.height;
if (currentOrientation == UIInterfaceOrientationPortrait){
// If not upside-down move down the origin.
newViewFrame.origin.y += statusBarFrame.size.height;
}
} else { // Is landscape / Slightly trickier.
// For portrait we shink width (for status bar on side of window)
newViewFrame.size.width -= statusBarFrame.size.width;
if (currentOrientation == UIInterfaceOrientationLandscapeLeft){
// If the status bar is on the left side of the window we move the origin over.
newViewFrame.origin.x += statusBarFrame.size.width;
}
}
}
// Animate... Play with duration later...
[UIView animateWithDuration:0.35 animations:^{
view.frame = newViewFrame;
}];
}
#end

Why are you calling this in viewDidLoad?
Try it in loadView?
- (void)loadView {
[super loadView];
[[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationSlide];
}

Yes, moving the view 20 pixels should fix your problem. The black is absence of anything to display, not an actual black bar.
As for potential status height changes, this fix will not work if that happens because the view will be moved by the height of the new status bar. If that happens, you will have to either add different offsets for different status bars, or find a completely new solution.

In viewWillAppear:
[[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationFade];
In viewDidAppear, you can insert:
self.view.window.rootViewController.view.frame = [UIScreen mainScreen].applicationFrame;
In viewWillDisappear:
[[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationFade];

I was able to fix this issue by calling:
[[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationSlide];
as others recommended before presenting the view controller that is displaying the black bar.
For example, if I had an action that presented ViewController, I would call it like so:
- (IBAction)presentViewController:(id)sender {
[[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationSlide];
ViewController *vc = [[ViewController alloc] initWithNibName:#"ViewController" bundle:nil];
[self presentViewController:vc animated:YES completion:nil];
}

Related

How to fade out the status bar while not hiding it

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.
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.
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:
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.
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.
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
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:
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];
}
}

want to Implement Sliding side menu with sliding status bar also. in iOS

I am interested to implement left side menu in my application I have used NVSlideMenuController for it. and it works fine.
But I want to modify it. I want to slide status bar with contentViewController and don't want status bar on MenuViewController.
currently it will look like below image
and I want to same as below image
Thanks In advance
You can try to turn off your 3G on second image, you will notice that statusBar didn't update.
It seems like a new api in iOS7
[[UIScreen mainScreen] snapshotViewAfterScreenUpdates:NO];
This may be the same question you ask, and there are a demo to show what you want.
Moving status bar in iOS 7
You may be able to do this by chaining the frame of the keyWindow (you can get this with [[UIApplication sharedApplication] keyWindow] (UIWindow is a subclass of UIView). This should move everything right, and you may need to make another UIWindow to bring other stuff on from the left.
You're not going to be able to move the status bar. The best you can do is hide it for that view controller:
- (void) viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
if (animated)
{
[[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationSlide];
}
else
{
[[UIApplication sharedApplication] setStatusBarHidden:YES];
}
}
- (void) viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
if (animated)
{
[[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationSlide];
}
else
{
[[UIApplication sharedApplication] setStatusBarHidden:NO];
}
}
Try playing around with the different values of UIStatusBarAnimation to see what looks best. There are three values: UIStatusBarAnimationNone, UIStatusBarAnimationFade, and UIStatusBarAnimationSlide`.
This's not an answer but a reply to James's answer because the formatting breaks on comment.
The fact is that setting the frame of the keyWindow will not be able to move the status bar. By setting a break point and printing out the keyWindow recursive description, we'll notice there's no status bar info inside.
(lldb) po [[[UIApplication sharedApplication] keyWindow] recursiveDescription]
<UIWindow: 0x8c5bf80; frame = (0 0; 320 480); gestureRecognizers = <NSArray: 0x8c5c500>; layer = <UIWindowLayer: 0x8c5c0a0>>
| <UIView: 0x8b49700; frame = (0 0; 320 480); autoresize = W+H; layer = <CALayer: 0x8b496b0>>
(lldb)
Follow Chris's link to Simon's answer does the job

Add Text in Statusbar [iOS Cydia App] [duplicate]

Is it possible to add a UIView on the staus bar of size (320 x 20)? I don't want to hide the status bar, I only want to add it on top of the status bar.
You can easily accomplish this by creating your own window above the existing status bar.
Just create a simple subclass of UIWindow with the following override of initWithFrame:
#interface ACStatusBarOverlayWindow : UIWindow {
}
#end
#implementation ACStatusBarOverlayWindow
- (id)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
// Place the window on the correct level and position
self.windowLevel = UIWindowLevelStatusBar+1.0f;
self.frame = [[UIApplication sharedApplication] statusBarFrame];
// Create an image view with an image to make it look like a status bar.
UIImageView *backgroundImageView = [[UIImageView alloc] initWithFrame:self.frame];
backgroundImageView.image = [UIImage imageNamed:#"statusBarBackground.png"];
[self addSubview:backgroundImageView];
[backgroundImageView release];
// TODO: Insert subviews (labels, imageViews, etc...)
}
return self;
}
#end
You can now, for example in a view controller in your application, create an instance of your new class and make it visible.
overlayWindow = [[ACStatusBarOverlayWindow alloc] initWithFrame:CGRectZero];
overlayWindow.hidden = NO;
Be aware of messing with the window key status by using - (void)makeKeyAndVisible or similar. If you make your main window (the UIWindow in your Application Delegate) loose key status, you will encounter problems with scrolling scrollviews to top when tapping the status bar etc.
I wrote a static library mimicing Reeders status bar overlay, you can find it here: https://github.com/myell0w/MTStatusBarOverlay
It currently supports iPhone and iPad, default and opaque black status bar styles, rotation, 3 different anymation modes, history-tracking and lots of more goodies!
Feel free to use it or send me a Pull Request to enhance it!
All answers looks like working, but in iOS6.0 I have next problems:
1/ Rotations looks bad
2/ Window (status bar is kind of Window) needed rootViewController
I'm using answer from myell0w, but rotate works not good. I've just remove one extra window and using UIWindow from AppDelegate to implement status bar.
May be this solution is ok only for one UIViewController-app...
Ive implemented by the next way:
1/ In ApplicationDelegate:
self.window.windowLevel = UIWindowLevelStatusBar + 1;
self.window.backgroundColor = [UIColor clearColor];
self.window.rootViewController = _journalController;
2/ Create custom UIView and implement all that you need inside:
For an example touchable statusbar:
#interface LoadingStatusBar : UIControl
And easily create and add to your controller view:
_loadingBar = [[LoadingStatusBar alloc] initWithFrame:topFrame];
[self addSubview:_loadingBar];
3/ Some magic when add your controller view (in initWithFrame:)
CGRect mainFrame = self.bounds;
mainFrame.origin.y = 20;
self.bounds = mainFrame;
Your controller view will has 2 views - content view and status bar view. You can show status bar, or hide it when you want.
Frame of content view will be:
_contentView.frame = CGRectMake(0, 20, self.bounds.size.width, self.bounds.size.height);
4/ And one last magic here :)
To detect touches in non touchable area I've used:
-(id)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
if (point.y < 20) return _loadingBar;
return [super hitTest:point withEvent:event];
}
For now it works fine on iPad/iPhone and all iOS's from 4 to 6.
Just to dismiss the "You cannot do this comments"...
I don't know how but I know it is doable. The Feed reader app called Reeder does that.
As you can see from the screenshot, Reeder puts a small dot on the top right of the screen. When you tap it. The bar will fill the whole statusbar until you tap it again to make it small.
First of all, a big thank you to #Martin Alléus for providing the code for this implementation.
I'm just posting for a problem that I faced and the solution I used, as I believe others might experience the same issue.
If the App is started while an call is in place, the status bar height will be 40 pixels and this means that the custom status bar will be initialized with that height.
But if the call is ended while you are still in the app, the status bar height will remain still 40 pixels and it will look weird.
So the solution is simple: I've used the Notification center to subscribe to the status bar frame change delegate of the app and adjust the frame:
- (void)application:(UIApplication *)application didChangeStatusBarFrame:(CGRect)oldStatusBarFrame {
//an in call toggle was done
//fire notification
[[NSNotificationCenter defaultCenter] postNotificationName:kStatusBarChangedNotification object:[NSValue valueWithCGRect:oldStatusBarFrame]];
}
And in the ACStatusBarOverlayWindow we subscribe to the notification:
-(id)initWithFrame:(CGRect)frame
{
if ((self = [super initWithFrame:frame]))
{
// Place the window on the correct level & position
self.windowLevel = UIWindowLevelStatusBar + 1.0f;
self.frame = [UIApplication sharedApplication].statusBarFrame;
self.backgroundColor = [UIColor blackColor];
//add notification observer for in call status bar toggling
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(statusBarChanged:) name:kStatusBarChangedNotification object:nil];
}
return self;
}
and our code to adjust the frame:
- (void)statusBarChanged:(NSNotification*)notification {
//adjust frame...
self.frame = [UIApplication sharedApplication].statusBarFrame;
//you should adjust also the other controls you added here
}
The kStatusBarChangedNotification is just a constant I've used for easy referrence, you can simply replace it with a string, or declare the constant globally.

touchesBegan after didRotateFromInterfaceOrientation

I noticed that after an orientation change from portrait to landscape, I'm not getting touchesBegan events for some parts of my view any longer. I suppose that this is because I'm not informing my UIView about the dimension change of my window's frame after the device rotation.
I'm setting up everything programmatically (UIWindow, UIViewController, UIView) like this:
myViewController = [[myUIViewController alloc] init];
myWindow = [[UIWindow alloc] initWithFrame: rect];
myView = [[myUIView alloc] initWithFrame: [myWindow bounds]];
[myViewController setView:myView];
[myWindow addSubview:myView];
[myWindow setFrame:rect];
[myWindow makeKeyAndVisible];
When I get the didRotateFromInterfaceOrientation notification, I'm updating the window frame like this:
[[[self view] window] setFrame:rect];
But after that, my UIView does no longer get touchesXXX events for all areas. It seems that only the areas of the previous frame are still reporting events. So my question: Is there anything else I need to do in didRotateFromInterfaceOrientation to inform my UIView about the dimension change?
Thanks for help!
EDIT: Do I have to reposition the UIView on didRotateFromInterfaceOrientation() or is this done automatically? I noticed that the "transform" property of my UIView is set to a transformation matrix when the orientation changes. However, this makes it very hard to reposition my view. The docs say that the "frame" property can't be used when a transformation is active, so I tried to modify the "center" property to reposition my view, but this also doesn't work correctly. I want to move the view to the top-left corner, so I set "center" to (screenwidth/2,screenheight/2) but it doesn't position the view correctly :( Any idea or info what must be done to get the events right in orientation mode?
I have had this same problem, and I believe it is a frame issue.
I had to manually set the rects depending on orientation, as well as set the userInteractionEnabled on the main view, like:
if (appOrientation == 0)
appOrientation = [UIApplication sharedApplication].statusBarOrientation;
if (appOrientation == UIInterfaceOrientationLandscapeLeft || appOrientation == UIInterfaceOrientationLandscapeRight) {
[[UIApplication sharedApplication] setStatusBarHidden:YES];
myView.frame = CGRectMake(0, 0, 1024, 768);
} else {
[[UIApplication sharedApplication] setStatusBarHidden:YES];
myView.frame = CGRectMake(0, 0, 768, 1024);
}
myView.userInteractionEnabled = YES;
OK I know this is an old question but this is how I fixed this issue.
In my situation I had a storyboard with a view that would be displayed either in portrait or forced to landscape mode depending on a user setting.
My app displays the statusBar at all times except for when I'm showing this view.
To make this all work, the transformations had to be applied in the viewWillAppear method for one. I had the following code in the viewDidAppear method at first and that messed with the bounds for the touchesBegan event I guess.
Here's the viewWillAppear code:
- (void)viewWillAppear:(BOOL)animated
{
// Hide the status bar
[[UIApplication sharedApplication] setStatusBarHidden:YES];
// This is to offset the frame so that the view will take the fullscreen once the status bar is hidden
self.navigationController.navigationBar.frame = CGRectOffset(self.navigationController.navigationBar.frame, 0.0, -20.0);
// If view is to be displayed in landscape mode
if ([[NSUserDefaults standardUserDefaults] boolForKey:#"orientation"])
{
// Change status bar orientation
[[UIApplication sharedApplication] setStatusBarOrientation:UIDeviceOrientationLandscapeLeft];
// Turn the view 90 degrees
[self.navigationController.view setTransform:CGAffineTransformMakeRotation(M_PI/2)];
// Set the frame and the bounds for the view
// Please Note: Frame size has to be reversed for some reason.
[self.navigationController.view setFrame: CGRectMake(0,0,320,480)];
[self.navigationController.view setBounds: CGRectMake(0,0,480,320)];
// Make sure user can interact with view
[self.navigationController.view setUserInteractionEnabled:YES];
}
}
Any other thing that had to happen layout wise, have to happen in the viewDidAppear method. For instance I had an image that covered the whole view and the frame had to be set depending on the orientation. Setting the frame in the viewWillAppear gave weird results but the same code worked perfectly in viewDidAppear.
Hope this helps someone as I banged my head for 6 hours on this thing.

How to make a view which covers whe whole screen, including the status bar?

I want to make an overlay which is partially transparent, and covers the entire screen including the status bar. I've seen that the folks at tapbots do exactly that. So it must be possible somehow. Status bar should still be visible!
Before iPhoneOS 3.2:
[[UIApplication sharedApplication] setStatusBarHidden:YES animated:NO];
after iPhoneOS 3.2:
[[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation: UIStatusBarAnimationNone];
For more information on these, see the documentation for UIApplication.
There are two ways to hide the status bar:
Programaticaly at runtime by using UIApplication sharedApplication:
- (void)setStatusBarHidden:(BOOL)hiddenwithAnimation:(UIStatusBarAnimation_)animation
Or at design time using the Info.plist property UIStatusBarHidden yes/no value.
You could try creating a full-size view and adding it as a subview of your main window. Something like:
CGRect mainWindowSize = [UIScreen mainScreen].bounds;
UIView* overlay = [[UIView alloc] initWithFrame:mainWindowSize];
// Use colorWithRed:green:blue:alpha: or a solid color then manually tweak alpha
overlay.backgroundColor = [UIColor redColor];
overlay.alpha = 0.2; // transparency level
overlay.userInteractionEnabled = YES;
// Add it on top of the main window
UIWindow* mainWindow = (((MyAppDelegate*)
[UIApplication sharedApplication].delegate).window);
[mainWindow addSubview:overlay];
Caveats: You may have to manually hide the status bar. Also, this overlay view and its subviews are going to get all the user tap events. May want to make sure that's what you want.
push the view controller as a modalViewController....
if your view controller is AVC and assuming you use a navigation controller:
[self presentModalViewController:AVC animated:YES];
from the current view controller you're on.