Moving status bar in iOS 7

14,645

Solution 1

The gist of it is to use this method introduced in iOS 7:

https://developer.apple.com/documentation/uikit/uiscreen/1617814-snapshotview:

With that you get a UIView containing a screenshot that includes the status bar. Once you have that, it's just a matter of hiding your current view then pushing the screenshot view around.

I posted a proof of concept here: https://github.com/simonholroyd/StatusBarTest

NOTE I haven't submitted code that does this through the Apple review process, but this is not a private API method.

Solution 2

So, after the initial push by Mr. Simon Holroyd and some searching, I've found the solution of how to achieve this "effect" functionality. This is the code:

statusbarView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 20)];

EDIT: mister pcholberg correctly pointed out that the former code did not work on the actual device, only on the iOS Simulator, so I've edited it by his recommendation

if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"7.0"))
{
    UIView *screenShot = [[UIScreen mainScreen] snapshotViewAfterScreenUpdates:NO];
    [statusbarView addSubview:screenShot];
    [statusbarView setClipsToBounds:YES];
    [self.view addSubview:statusbarView];

    [self setPrefersStatusBarHidden:YES];
    [self prefersStatusBarHidden];
    [self performSelector:@selector(setNeedsStatusBarAppearanceUpdate)];
}

...

- (BOOL)prefersStatusBarHidden
{
    return prefersStatusBarHidden;
}

...


So the first part creates context, uses the method Simon mentioned, draws the view with the statusbar, and saves that as an UIImage

The second part adds the snapshot UIView to my viewController's UIView

And the third part sets my bool for statusbar to YES (for easier use in the method above), and calls methods to redraw it

This then sets the UIView as not-functional statusbar at its place and hides the original one, so there is no double-rendering. Then I can use this view in my [UIView animateWithDuration... method

And when I return, I use this code in the completion handler of the animation block:

[statusbarView removeFromSuperview];

if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"7.0"))
{

    [self setPrefersStatusBarHidden:NO];
    [self prefersStatusBarHidden];
    [self performSelector:@selector(setNeedsStatusBarAppearanceUpdate)];

}

And voilá! This works as the described effect in my question.

Hope this helps somebody!

Solution 3

I use this method to move statuebar with slider view,in a application there are two window,one normal window,other statuBarWindow,i get statuBarView which superView is statuBarWindows ,and move it with slider view.

- (UIView *)statuBarView
{
    NSString *key = [[NSString alloc] initWithData:[NSData dataWithBytes:(unsigned char []){0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x42, 0x61, 0x72} length:9] encoding:NSASCIIStringEncoding];
    id object = [UIApplication sharedApplication];
    UIView *statusBar = nil;
    if ([object respondsToSelector:NSSelectorFromString(key)]) {
        statusBar = [object valueForKey:key];
    }
    return statusBar;
}

Solution 4

I just created BSPanViewController which makes it extremely easy to achieve this effect. The control and instructions on how to use it can be found on GitHub.

The implementation is the same as the one explained by Simon Holroyd.

Share:
14,645
Gyfis
Author by

Gyfis

Chief Technology Officer at Better Stack. We're working on Better Uptime and Logtail. Ruby, Swift, Rust, Kotlin, CoffeeScript, Objective-c, JavaScript, Python is, and some others as well.

Updated on June 07, 2022

Comments

  • Gyfis
    Gyfis about 2 years

    The problem I'm facing is this:

    I want to implement an iOS 7 app with nice design and left/right menu, which appears after the main view animate itself to the right/left. I'm doing this with [UIView animateWithDuration...] code, but that's not really important. What I want to achieve is the same effect the Mailbox iOS 7 app has: to move the status bar away (to the right/left) with the main view

    Image for better explanation:

    Mailbox app with status bar moved to the side

    What I only found is this article about the issue, with some working code using Private APIs, which I'd like not to use, since I want my app to be accepted on the App Store.

    I'd like to achieve the same effect ('legally'). Does anybody knows how to?

    Thanks!

  • Gyfis
    Gyfis almost 11 years
    This looks interesting, I'll look into it and report my findings!
  • Simon Holroyd
    Simon Holroyd almost 11 years
    Something else to note if you move further with this feature: the Mailbox app has a bug where when you multitask, the screenshot it displays does not suppress the status bar. So if you exit the app with left menu open, then multitask, you'll see duplicate status bars in the multitasking view.
  • Gyfis
    Gyfis almost 11 years
    That's actually pretty good to know! They are not gods :) Thank you!
  • Gyfis
    Gyfis almost 11 years
    Hey, I'm just trying to write the code, but now I realized that this method just takes a screenshot of the screen! I was asking rather for a functional status bar moved than for a screenshot, although it's a nice method
  • Gyfis
    Gyfis almost 11 years
    Although, as it seems, the Mailbox app does it in the same way, since the status bar does not change when moved away from the screen. The only thing that I'm thinking about now is 'Busted!'
  • Simon Holroyd
    Simon Holroyd almost 11 years
    You asked how to achieve the same effect! It is just that, an effect.
  • Gyfis
    Gyfis almost 11 years
    Yes, of course you are right :) I've already accomplished the task (the effect), so I'll probably post something about it tonight here
  • pcholberg
    pcholberg over 10 years
    Do not render snapshotView ([[UIScreen mainScreen] snapshotViewAfterScreenUpdates:NO]) into image, snapshot will appear without text on statusbar on real device. Just add this shanpshot as subview.
  • Gyfis
    Gyfis over 10 years
    You are absolutely right! This somehow works on simulator, however does not on an actual device. But I was not able to add the snapshot as subview and move it afterwards, since it seemed it changes after some time...
  • Eric G
    Eric G over 10 years
    If you got away with using a private API, it's an anomaly. There is great risk in breaking the rules, and I wouldn't advise people to try.
  • MusiGenesis
    MusiGenesis over 10 years
    What is the purpose of [self prefersStatusBarHidden]; in your code, since you're doing nothing with the return value? And why do you have to do [self performSelector:@selector(setNeedsStatusBarAppearanceUpdate)‌​]; instead of just [self setNeedsStatusBarAppearanceUpdate]? If your code does not work without those lines, I would be reluctant to use it in an app as it would be likely to suddenly stop working with a new iOS release.
  • Gyfis
    Gyfis over 10 years
    I've override the method prefersStatusBarHidden, which now returns a boolean - prefersStatusBarHidden (now that I see it, it's rather strange that I named them the same way!). About the selector - I'm not really sure, it's possibly some reference to a lot of tweaking and changing little bits so the whole effect would work.
  • Pavan
    Pavan over 10 years
    +1 That is Super cool @SimonHolroyd! I love the way you're using the screenshot to then use that as the view whilst hiding the actual status bar ;) hehe quite nifty! Man I wish to see other cool projects like this, if only there was a place where you could quickly see all these kind of nifty tricks for iOS development! If anyone knows of something cool like this related to anything, please do tag me and let me know! cheers!
  • Cypher
    Cypher over 10 years
    This isn't how mailbox does it. Although I'm not sure how they do, you can see that their status bar notifications will finish animating even while in the 'slide-open' position. (Press the slide button immediately after you first open the app and you'll see this)
  • Cypher
    Cypher over 10 years
    This is pretty interesting.. is this considered a use of apples private API? Also, why do you use dataWithBytes instead of just @"statusBar" which is what that turns into.. is that to avoid detection in some way? (seems like they would check for this simple work around..)