在iOS 7中,UIStatusBar被设计成这样,它与视图合并:
(GUI由Tina tavvar设计)
It is cool, but it will somewhat mess up your view when you have something at the top part of your view, and it becomes overlapped with the status bar.
Is there a simple solution (such as setting a property in info.plist) that can change the way it works [not overlapping] back to how it is in iOS6?
I know a more straightforward solution is to have self.view.center.x + 20 points for every single view controller, but changing them will screw other dimensions up (having a different self.view.center.x can cause problem to custom segues, etc.) and suddenly it turns into a tedious job that is best to be avoided.
I'll really be glad if someone can provide me an one-liner solution for this.
附注:我知道我可以隐藏状态栏
[[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationNone];
在didFinishLaunchingWithOptions方法中,但这是一个变通方法,一个避免问题的快捷方式,所以我不认为这是一个真正的解决方案。
这是我写的一篇博客文章的交叉发布,但这里是关于iOS 7上的状态栏、导航栏和容器视图控制器的完整概述:
There is no way to preserve the iOS 6 style status bar layout. The status bar will always overlap your application on iOS 7
Do not confuse status bar appearance with status bar layout. The appearance (light or default) does not affect how the status bar is laid out (frame/height/overlap). It is important to note as well that the system status bar no longer has any background color. When the API refers to UIStatusBarStyleLightContent, they mean white text on a clear background. UIStatusBarStyleDefault is black text on a clear background.
Status bar appearance is controlled along one of two mutually-exclusive basis paths: you can either set them programmatically in the traditional manner, or UIKit will update the appearance for you based on some new properties of UIViewController. The latter option is on by default. Check your app’s plist value for “ViewController-Based Status Bar Appearance” to see which one you’re using. If you set this value to YES, every top-level view controller in your app (other than a standard UIKit container view controller) needs to override preferredStatusBarStyle, returning either the default or the light style. If you edit the plist value to NO, then you can manage the status bar appearance using the familiar UIApplication methods.
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.
If you supply a custom navigation bar background image that is only 44 points (88 pixels) tall, and the UINavigationController’s view’s bounds matches the UIWindow’s bounds (as discussed in #4), the UINavigationController will draw your image in the frame (0,20,320,44), leaving 20 points of opaque black space above your custom image. This may confuse you into thinking you are a clever developer who bypassed rule #1, but you are mistaken. The navigation bar is still 64 points tall. Embedding a UINavigationController in a slide-to-reveal style view hierarchy makes this abundantly clear.
Beware of the confusingly-named edgesForExtendedLayout property of UIViewController. Adjusting edgesForExtendedLayout does nothing in most cases. The only way UIKit uses this property is if you add a view controller to a UINavigationController, then the UINavigationController uses edgesForExtendedLayout to determine whether or not its child view controller should be visible underneath the navigation bar / status bar area. Setting edgesForExtendedLayout on the UINavigationController itself does nothing to alter whether or not the UINavigationController has a 44 or 64 point high navigation bar area. See #4 for that logic. Similar layout logic applies to the bottom of your view when using a toolbar or UITabBarController.
If all you are trying to do is prevent your custom child view controller from underlapping the navigation bar when inside a UINavigationController, then set edgesForExtendedLayout to UIRectEdgeNone (or at least a mask that excludes UIRectEdgeTop). Set this value as early as possible in the life cycle of your view controller.
UINavigationController and UITabBarController will also try to pad the contentInsets of table views and collection views in its subview hierarchy. It does this in a manner similar to the status bar logic from #4. There is a programmatic way of preventing this, by setting automaticallyAdjustsScrollViewInsets to NO for your table views and collection views (it defaults to YES). This posed some serious problems for Whisper and Riposte, since we use contentInset adjustments to control the layout of table views in response to toolbar and keyboard movements.
To reiterate: there is no way to return to iOS 6 style status bar layout logic. In order to approximate this, you have to move all the view controllers of your app into a container view that is offset by 20 points from the top of the screen, leaving an intentionally black view behind the status bar to simulate the old appearance. This is the method we ended up using in Riposte and Whisper.
Apple is pushing very hard to ensure that you don’t try to do #9. They want us to redesign all our apps to underlap the status bar. There are many cogent arguments, however, for both user experience and technical reasons, why this is not always a good idea. You should do what is best for your users and not simply follow the whimsy of the platform.
对于Archy Holt的回答,有一个小的选择,更简单一点:
a.在info.plist中设置“UIViewControllerBasedStatusBarAppearance”为“NO”
b.在AppDelegate的应用程序didFinishLaunchingWithOptions:中调用:
if ([[UIDevice currentDevice].systemVersion floatValue] < 7)
{
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
}
else
{
// handling statusBar (iOS7)
application.statusBarStyle = UIStatusBarStyleLightContent;
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].applicationFrame];
self.window.clipsToBounds = YES;
// handling screen rotations for statusBar (iOS7)
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidChangeStatusBarOrientationNotification:) name:UIApplicationDidChangeStatusBarOrientationNotification object:nil];
}
并添加方法:
- (void)applicationDidChangeStatusBarOrientationNotification:(NSNotification *)notification
{
// handling statusBar (iOS7)
self.window.frame = [UIScreen mainScreen].applicationFrame;
}
你也可以考虑子类化UIWindow来处理UIApplicationDidChangeStatusBarOrientationNotification本身。
这可能是一个压倒性的问题,如果你使用自动布局,因为你不能直接操作帧。有一个简单的解决方案,不需要太多的工作。
我最终在一个实用工具类中编写了一个实用方法,并从所有视图控制器的viewDidLayoutSubviews方法中调用它。
+ (void)addStatusBarIfiOS7:(UIViewController *)vc
{
if (NSFoundationVersionNumber > NSFoundationVersionNumber_iOS_6_1) {
CGRect viewFrame = vc.view.frame;
if(viewFrame.origin.y == 20) {
//If the view's y origin is already 20 then don't move it down.
return;
}
viewFrame.origin.y+=20.0;
viewFrame.size.height-= 20.0;
vc.view.frame = viewFrame;
[vc.view layoutIfNeeded];
}
}
重写视图控制器中的viewDidLayoutSubviews方法,在那里你想要状态栏。它会让你通过自动布局的负担。
- (void)viewDidLayoutSubviews
{
[[UIApplication sharedApplication]setStatusBarStyle:UIStatusBarStyleLightContent];
[super viewDidLayoutSubviews];
[MyUtilityClass addStatusBarIfiOS7:self];
}
更新(新解决方案)
本次更新是iOS 7导航栏问题的最佳解决方案。您可以设置导航栏颜色示例:FakeNavBar。backgroundColor = [UIColor redColor];
注意:如果你使用默认的导航控制器,请使用旧的解决方案。
AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
if(NSFoundationVersionNumber >= NSFoundationVersionNumber_iOS_7_0)
{
UIView *FakeNavBar = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 20)];
FakeNavBar.backgroundColor = [UIColor whiteColor];
float navBarHeight = 20.0;
for (UIView *subView in self.window.subviews) {
if ([subView isKindOfClass:[UIScrollView class]]) {
subView.frame = CGRectMake(subView.frame.origin.x, subView.frame.origin.y + navBarHeight, subView.frame.size.width, subView.frame.size.height - navBarHeight);
} else {
subView.frame = CGRectMake(subView.frame.origin.x, subView.frame.origin.y + navBarHeight, subView.frame.size.width, subView.frame.size.height);
}
}
[self.window addSubview:FakeNavBar];
}
return YES;
}
旧的解决方案-如果你使用以前的代码,请忽略下面的代码和图像
这是旧版本的iOS 7导航栏解决方案。
我用以下代码解决了这个问题。这是用来添加状态栏的。
didFinishLaunchingWithOptions
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0) {
UIView *addStatusBar = [[UIView alloc] init];
addStatusBar.frame = CGRectMake(0, 0, 320, 20);
addStatusBar.backgroundColor = [UIColor colorWithRed:0.973 green:0.973 blue:0.973 alpha:1]; //change this to match your navigation bar
[self.window.rootViewController.view addSubview:addStatusBar];
}
对于界面构建器,这是在iOS 6中打开的;它从0像素开始。
注意:iOS 6/7只有在详细信息窗格的“文件检查器”(最左边的图标)中取消视图控制器的“使用自动布局”时才会出现增量。