441

我最近下载了Xcode 5 DP 以在 iOS 7 中测试我的应用程序。我注意到并确认的第一件事是我的视图边界并不总是调整大小以适应状态栏和导航栏。

viewDidLayoutSubviews中,我打印视图的边界:

{{0, 0}, {320, 568}}

这导致我的内容出现在导航栏和状态栏下方。

我知道我可以通过获取主屏幕的高度,减去状态栏的高度和导航栏的高度来计算自己的高度,但这似乎是不必要的额外工作。

我该如何解决这个问题?

更新:

我已经找到了解决这个特定问题的方法。将导航栏的 translucent 属性设置为 NO:

self.navigationController.navigationBar.translucent = NO;

这将修复视图在导航栏和状态栏下方的框架。

但是,当您希望导航栏为半透明时,我还没有找到解决方法。例如,在全屏查看照片时,我希望导航栏是半透明的,并且视图被框在它下面。这行得通,但是当我切换显示/隐藏导航栏时,我遇到了更奇怪的结果。第一个子视图(一个 UIScrollView)每次都会改变它的边界 y 原点。

4

20 回答 20

498

您可以通过实现edgesForExtendedLayout在 iOS7 SDK 中调用的新属性来实现这一点。请添加以下代码来实现这一点,

if ([self respondsToSelector:@selector(edgesForExtendedLayout)])
        self.edgesForExtendedLayout = UIRectEdgeNone;

您需要在-(void)viewDidLoad方法中添加上述内容。

iOS 7 对您布局和自定义UI外观的方式进行了一些更改。视图控制器布局、色调颜色和字体的更改会影响应用程序中的所有UIKit对象。此外,手势识别器 API 的增强功能使您可以更精细地控制手势交互。

使用视图控制器

在 iOS 7 中,视图控制器使用全屏布局。同时,iOS 7 让您可以更精细地控制视图控制器布局其视图的方式。特别是,全屏布局的概念得到了改进,让视图控制器指定其视图每个边缘的布局。

视图控制器属性在 iOS 7 中已弃用。wantsFullScreenLayout如果您当前指定wantsFullScreenLayout = NO,则视图控制器在 iOS 7 中运行时可能会在意外的屏幕位置显示其内容。

要调整视图控制器如何布置其视图,UIViewController 请提供以下属性:

  • edgeForExtendedLayout

edgesForExtendedLayout属性使用UIRectEdge类型,除了指定 none 和 all 之外,还指定矩形的四个边中的每一个。用于edgesForExtendedLayout指定应扩展视图的哪些边缘,而不考虑条形半透明度。默认情况下,此属性的值为UIRectEdgeAll

  • extendedLayoutIncludesOpaqueBars

如果您的设计使用不透明条,请将属性 edgesForExtendedLayout设置为NO进行优化。(默认值为NO。)extendedLayoutIncludesOpaqueBarsextendedLayoutIncludesOpaqueBars

  • 自动调整ScrollViewInsets

如果您不希望自动调整滚动视图的内容插图,请设置automaticallyAdjustsScrollViewInsetsNOautomaticallyAdjustsScrollViewInsets(默认值为YES。)

  • 顶部布局指南,底部布局指南

topLayoutGuidebottomLayoutGuide属性指示视图控制器视图中顶部或底部条边的位置。topLayoutGuide如果栏应该与视图的顶部或底部重叠,您可以使用 Interface Builder 通过在底部布局指南的底部或顶部创建约束来相对于栏定位视图。(如果没有条应该与视图重叠,则底部与 topLayoutGuide视图顶部相同,顶部与视图 bottomLayoutGuide底部相同。)这两个属性都是在请求时延迟创建的。

请参考,苹果文档

于 2013-09-13T11:52:01.243 回答
112

您不必计算将所有内容向下移动多远,为此有一个内置属性。在 Interface Builder 中,突出显示您的视图控制器,然后导航到属性检查器。在这里,您会在“扩展边缘”一词旁边看到一些复选框。如您所见,在第一个屏幕截图中,默认选择是内容显示在顶部和底部栏下,而不是在不透明栏下,这就是为什么将栏样式设置为不透明对您有用的原因。

正如您在第一个屏幕截图中所看到的,导航栏下方隐藏着两个 UI 元素。(我在 IB 中启用了线框来说明这一点)这些元素,一个 UIButton 和一个 UISegmentedControl 都将它们的“y”原点设置为零,并且视图控制器设置为允许顶部栏下方的内容。

在此处输入图像描述

第二个屏幕截图显示了当您取消选中“在顶部栏下”复选框时会发生什么。如您所见,视图控制器视图已适当向下移动,使其 y 原点位于导航栏的正下方。

在此处输入图像描述

这也可以通过使用-[UIViewController edgesForExtendedLayout]. 这是edgeForExtendedLayoutUIRectEdge的类参考链接

[self setEdgesForExtendedLayout:UIRectEdgeNone];
于 2013-09-13T11:56:25.280 回答
33

我以编程方式创建了我的视图,这最终对我有用:

- (void) viewDidLayoutSubviews {
    // only works for iOS 7+
    if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0) {
        CGRect viewBounds = self.view.bounds;
        CGFloat topBarOffset = self.topLayoutGuide.length;

        // snaps the view under the status bar (iOS 6 style)
        viewBounds.origin.y = topBarOffset * -1;

        // shrink the bounds of your view to compensate for the offset
        viewBounds.size.height = viewBounds.size.height + (topBarOffset * -1);
        self.view.bounds = viewBounds;
    }
}

来源(在pg.39底部的 topLayoutGuide 部分)。

于 2013-09-24T08:35:44.290 回答
32

Swift 3 / Swift 4 解决方案也适用于 iOS 10+ 中的 NIB/XIB 文件:

override func viewDidLoad() {
    super.viewDidLoad()

    edgesForExtendedLayout = []
}
于 2016-09-01T20:17:14.927 回答
10

如果您希望视图具有半透明导航栏(这很好),您必须设置 contentInset 或类似的。

这是我的做法:

// Check if we are running on ios7
if([[[[UIDevice currentDevice] systemVersion] componentsSeparatedByString:@"."][0] intValue] >= 7) {
      CGRect statusBarViewRect = [[UIApplication sharedApplication] statusBarFrame];
      float heightPadding = statusBarViewRect.size.height+self.navigationController.navigationBar.frame.size.height;

      myContentView.contentInset = UIEdgeInsetsMake(heightPadding, 0.0, 0.0, 0.0);
}
于 2013-09-13T09:16:08.053 回答
9

在您的应用程序 plist 文件中添加一行,将其命名为“基于控制器的状态栏外观”并将其设置为NO

于 2013-09-19T13:45:32.030 回答
9

edgesForExtendedLayout对 iOS 7 有效。但是,如果您跨 iOS 7 SDK 构建应用程序并将其部署在 iOS 6 中,导航栏将显示为半透明,并且视图位于其下方。因此,要为 iOS 7 和 iOS 6 修复它,请执行以下操作:

self.navigationController.navigationBar.barStyle = UIBarStyleBlackOpaque;
if ([self respondsToSelector:@selector(edgesForExtendedLayout)])
    self.edgesForExtendedLayout = UIRectEdgeNone;   // iOS 7 specific
于 2013-09-21T05:18:57.170 回答
7

最简单的技巧是打开NIB文件并执行以下两个简单步骤:

  1. 只需切换它并将其设置为您喜欢的那个:

在此处输入图像描述

  1. 选择您想要下移的那些 UIView's/UIIMageView's/...。就我而言,只有徽标重叠,我将增量设置为 +15;(如果您在步骤 1 中选择了 iOS 7,则为 -15)

在此处输入图像描述

结果:_

前 后

于 2013-09-20T10:27:26.743 回答
5

快速解决方案:

override func viewWillAppear(animated: Bool) {
    super.viewWillAppear(animated)
    self.edgesForExtendedLayout = UIRectEdge.None
}
于 2015-07-21T17:06:30.220 回答
4

我想扩展 Stunner 的答案,并添加一个if语句来检查它是否是 iOS-7,因为当我在 iOS 6 上测试它时,我的应用程序会崩溃。

添加将添加:

if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0)

所以我建议将此方法添加到您的MyViewControler.m文件中:

- (void) viewDidLayoutSubviews {
    if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0) {
        CGRect viewBounds = self.view.bounds;
        CGFloat topBarOffset = self.topLayoutGuide.length;
        viewBounds.origin.y = topBarOffset * -1;
        self.view.bounds = viewBounds;
    }
}
于 2013-10-01T15:36:26.197 回答
4

斯威夫特 3

override func viewWillAppear(_ animated: Bool) {
    self.edgesForExtendedLayout = []
}
于 2017-07-04T07:27:21.193 回答
3

我有一个场景,我使用 Apple 编写的 BannerViewController 来显示我的广告和嵌入在 BannerViewController 中的 ScrollViewController。

为了防止导航栏隐藏我的内容,我必须进行两项更改。

1) 修改 BannerViewController.m

- (void)viewDidLoad
{
   [super viewDidLoad];
   float systemVersion = [[[UIDevice currentDevice] systemVersion] floatValue];
   if (systemVersion >= 7.0) {
      self.edgesForExtendedLayout = UIRectEdgeNone;
   }
}

2) 修改我的 ScrollViewContoller

- (void)viewDidLoad
{
    [super viewDidLoad];
    float systemVersion = [[[UIDevice currentDevice] systemVersion] floatValue];
    if (systemVersion >= 7.0) {
        self.edgesForExtendedLayout = UIRectEdgeBottom;
    }
}

现在广告正确地显示在视图的底部,而不是被导航栏覆盖,并且顶部的内容没有被截断。

于 2013-10-21T16:35:20.107 回答
2

只需在视图中设置以下代码就会出现。

  if ([[[UIDevice currentDevice] systemVersion] floatValue]<= 7) {
self.edgesForExtendedLayout = UIRectEdgeNone;
 }
于 2015-06-30T17:57:56.303 回答
2

斯威夫特 4.2 - Xcode 10.0 - iOS 12.0:

if #available(iOS 11.0, *) {} else {
  self.edgesForExtendedLayout = []
  self.navigationController?.view.backgroundColor = .white
}
于 2018-08-25T12:18:23.950 回答
2

像这样对顶部布局进行约束 在此处输入图像描述

于 2015-09-10T09:00:04.943 回答
1

对我来说,最简单的解决方案是在 plist 中添加两个键

在此处输入图像描述

于 2013-10-03T10:28:36.203 回答
1

将下拉列表中的“基于视图控制器的状态栏外观”键添加为info.plist. 像这样的东西:

在此处输入图像描述

于 2013-09-23T21:17:24.043 回答
1

我的 iPad 应用程序(armv7、armv7s、amr64)也遇到了同样的问题,只是通过展示另一个 UIViewController 并在解除它们后进入状态栏下的导航栏......我花了很多时间来寻找任何解决方案。我正在使用故事板和 UIViewController 的 InterfaceBuilder,这使得我从 FullScreen -> Current Context 设置 Presentation 并解决了这个问题。它仅适用于我的应用程序,适用于 iPads => iOS8.0(使用 iOS8.1 进行测试),并且适用于 iOS 7.1 的 iPad 不起作用!!看截图

于 2015-01-30T09:52:31.443 回答
0

在 iOS 7 中隐藏状态栏的步骤:

1.转到您的应用程序 info.plist 文件。

2.设置,查看基于控制器的状态栏外观:Boolean NO

希望我解决了状态栏问题.....

于 2014-01-30T11:46:13.300 回答
0

在我的情况下, loadView() 中断
了这段代码: self.edgesForExtendedLayout = UIRectEdgeNone

但删除 loadView() 后一切正常

于 2017-03-01T05:10:32.873 回答