iOS 7:UITableView显示在状态栏下


219

我的应用程序的第一个屏幕是一个UITableViewController没有导航栏的屏幕,这意味着内容在状态栏下流动,因此存在很多文本冲突。我已经调整了的属性,Under top bars并且Adjust scroll view insets实际上阻​​止了它的滚动,但是以保持表格视图的顶部为代价。我试图将UITableView框架设置为偏移20像素,但是它似乎没有生效,并且由于我目前需要该应用与iOS 6兼容,因此我无法跳至iOS 7故事板来强制使用自动布局顶部高度指南。有没有人找到适用于两个版本的解决方案?

我尝试过的操作:设置edgesForExtendedLayout,更改情节提要中Under top bars和的设置Adjust scroll view,将框架强制到新区域。

一张图片胜过千言万语: 下的状态栏流


2
在iOS 7上运行时快速变通可能是一个空白的20像素的头添加到表
埃里克斯

@EricS:我已经有一个UITableView标题了,它也在状态栏下流动。
尼古拉斯·史密斯

为什么不在iOS 6上使用自动布局指南?有用。
史蒂芬·费舍尔

Answers:


366

对于有兴趣复制此文件的任何人,只需执行以下步骤:

  1. 创建一个新的iOS项目
  2. 打开主故事板并删除默认/初始 UIViewController
  3. UITableViewController从对象库中拖出一个新对象
  4. 将其设置为初始视图控制器
  5. 向表格提供一些测试数据

如果按照上述步骤操作,则在运行该应用程序时,您将看不到任何内容,包括将Xcode的复选框调整为“在{Top,Bottom,Opaque} Bars下方延伸边缘”可阻止第一行显示在状态栏下方,您也无法以编程方式解决此问题。

例如,在上述情况下,下面将有没有效果:

// These do not work
self.edgesForExtendedLayout=UIRectEdgeNone;
self.extendedLayoutIncludesOpaqueBars=NO;
self.automaticallyAdjustsScrollViewInsets=NO;

这个问题可能非常令人沮丧,我相信这是Apple的错误,特别是因为它以自己的方式UITableViewController从对象库中预先显示出来。

我不同意使用任何形式的“魔术数字”来解决此问题的人,例如“使用20px的增量”。这种紧密耦合的编程绝对不是Apple希望我们在这里做的。

我发现了针对此问题的两种解决方案:

  • 保留UITableViewController的场景
    如果你想保持UITableViewController在故事板,而无需手动将它变成另一种观点,你可以嵌入UITableViewControllerUINavigationController在检查(编辑>嵌入>导航控制器),并取消选中‘显示导航栏’ 。这样就解决了这个问题,无需进行任何额外的调整,并且还可以将您UITableViewController的场景保留在情节提要中。

  • 使用AutoLayout并将嵌入UITableView到另一个视图中 (我相信这就是Apple希望我们这样做的方式)
    创建一个空白UIViewController并将其拖入UITableView其中。然后,从您的Ctrl键UITableView向状态栏拖动。当鼠标移到状态栏的底部时,您将看到一个自动布局气泡,上面显示“顶部布局指南”。释放鼠标,然后选择“垂直间距”。这将告诉布局系统将其放置在状态栏的正下方。

我已经在一个空的应用程序上测试了这两种方法,并且它们都可以工作。您可能需要做一些额外的调整才能使它们适用于您的项目。


5
仅供参考,选项1(嵌入在导航控制器中)使您在滚动表时在半透明状态栏后面可见单元格。
RyanR 2013年

4
@RyanR我也注意到了,但我认为在某些时候还可以。苹果在移动Safari的打开的标签页视图中做了同样的事情,标签页将显示在状态栏下方。导航控制器解决方案是最简单的解决方法,我对此深信不疑。但是,半透明的状态栏是一个愚蠢的主意。
骄傲颂

6
从表格视图进行控制拖动时,我永远不会遇到状态栏。视图中没有可见的状态栏,不确定您的意思吗……
Jesse

4
选项2 在Xcode 5中不再起作用。所描述的动作(ctrl-drag)应该永远不会起作用,因为该动作归Outlets系统所有,但是即使您手动拖动到左侧的“场景树”,顶部布局指南也不起作用(应该这样做,AFAICT-我相信这是AutoLayout中的另一个主要错误:( :()
亚当

3
@PrideChung在使用带有tableview的标头时看起来很愚蠢,标头保持固定在顶部,元素将消失在后面-并在透明状态栏后面重新出现...
Ixx

86

如果您以编程方式进行操作,并且在UITableViewController没有优势的情况下使用,UINavigationController最好的选择是在以下步骤进行操作viewDidLoad

迅捷3

self.tableView.contentInset = UIEdgeInsets(top: 20, left: 0, bottom: 0, right: 0)

早期的Swift

self.tableView.contentInset = UIEdgeInsetsMake(20.0f, 0.0f, 0.0f, 0.0f);

UITableViewController仍将滚动状态栏的后面,但是当滚动到顶部不会在它之下。


1
您可以添加OS版本> 7的检查,并且也支持较旧的OS(这些没有插入项)。否则,这对我来说很有效。
SimpsOff 2014年

8
硬编码高度值不是最好的主意。
MaciejKozieł14年

3
是的,改用topLayoutGuide长度。
lipka 2014年

10
我会用UIApplication.sharedApplication.statusBarFrame.size.height20代替
。– mojuba

这些天已更新为UIApplication.shared.statusBarFrame.size.height。
asetniop

23

请注意:这对我来说适用于以下配置:

  • 屏幕顶部没有导航栏(表格视图符合状态栏)
  • 表格视图不可滚动

如果以上两个要求均未达到,您的里程可能会有所不同。

原始帖子

我以编程方式创建了视图,最终为我工作:

- (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;
    }
}

源代码(在第39页底部的topLayoutGuide部分中)。


有趣的是...我可以在项目中这样称呼它而没有任何问题...但是我的视图布局结构可能比您更简单。您的建议是正确的,我会在答案中做出更改。
Stunner 2013年

旋转应用程序时,我偶然发现了这个问题。如果禁用了旋转功能,则可能不会有问题。
杰里米

我没有在项目中启用轮换功能,但遇到了问题,但是,这仍然是个好主意。
Stunner 2013年

1
对于实现自己的Container控制器的任何人,这都是调整状态或导航条重叠的正确方法!
拉斐尔·诺布雷

如果更改原点,但不更改视图边界的高度,那么屏幕底边是否会因更改原点的量而被裁剪掉底部?(因此无法看到最后几行像素)
Tenfour04

20

添加到最上面的答案:

在第二种方法最初似乎无效之后,我进行了一些其他修改,并找到了解决方案。

TLDR;最佳答案的第二个解决方案几乎可行,但是对于某些版本的xCode ctrl +拖动到“顶部布局指南”并选择“垂直间距”没有任何作用。但是,通过首先调整“表格视图”的大小,然后选择“从顶部空间到顶部布局指南”,可以工作


  1. 将空白的ViewController拖到情节提要上。 查看控制器

  2. 将UITableView 对象拖到视图中。(不是UITableViewController)。使用蓝色布局指南将其放置在最中间的位置。

表格检视 中心图片

  1. 将UITableViewCell拖到TableView中。这将是您的原型重用单元,因此不要忘记在“属性”选项卡下将其设置为“重用标识符”,否则会崩溃。

添加表格视图单元格 重用标识符

  1. 创建UIViewController的自定义子类,然后添加<UITableViewDataSource, UITableViewDelegate>协议。不要忘记在Identity Inspector中将情节提要的ViewController设置为此类。

  2. 在实现文件中为TableView创建一个插座,并将其命名为“ tableView”

创建出口 tableView

  1. 右键单击TableView,然后将数据源和委托都拖到ViewController中。

数据源委托

现在,对于不剪辑到状态栏中的部分。

  1. 抓住表格视图的顶部边缘,然后将其向下移动到靠近顶部的蓝色虚线自动布局指南之一

调整大小

  1. 现在,您可以控制从“表格视图”到顶部的拖动,然后选择“顶部空间到顶部布局指南”

顶部空间到顶部布局指南

  1. 它将为您提供有关TableView布局不明确的错误,只需添加“缺少约束”即可完成。

添加缺少的约束

现在,您可以像平常一样设置表格视图,并且不会剪切状态栏!


11
- (void) viewDidLayoutSubviews {
    if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_6_1) {
        self.navigationController.navigationBar.barStyle = UIBarStyleBlackOpaque;
        if ([self respondsToSelector:@selector(edgesForExtendedLayout)])
            self.edgesForExtendedLayout = UIRectEdgeNone;   // iOS 7 specific
        CGRect viewBounds = self.view.bounds;
        CGFloat topBarOffset = self.topLayoutGuide.length;
        viewBounds.origin.y = topBarOffset * -1;
        self.view.bounds = viewBounds;
        self.navigationController.navigationBar.translucent = NO;
    }
}

https://developer.apple.com/library/ios/documentation/userexperience/conceptual/TransitionGuide/SupportingEarlieriOS.html#//apple_ref/doc/uid/TP40013174-CH14-SW1


2
切换<=到>并删除空的if块
pfrank 13-10-28

1
阅读self.topLayoutGuide.lengthviewDidLayoutSubviews会搞砸的子视图的布局。

如您所描述的那样@ OLzh,self.tableView无法滚动。
DawnSong 2015年

8

在情节提要上选择UIViewController,然后取消选中“在顶部栏下扩展边缘”。为我工作。:)


las,对我没用。完全默认的UITableViewController仍与状态栏重叠。我认为状态栏不算是顶级栏。只是导航栏和标签栏。
Andrew Duncan

ty,这是唯一对我有用的解决方案。(请注意,您必须“缩短” IB状态栏中的表格视图并更新约束)。
马丁·马卡斯基

使用UITableViewController在Xcode 8上为我工作。
爱德华·巴比尔

8

这是在“ Swift”中编写它的方法。@lipka的答案的调整:

tableView.contentInset = UIEdgeInsetsMake(20.0, 0.0, 0.0, 0.0)

这里不允许急忙吗?
uplearnedu.com 2015年

唯一适合我的解决方案,并不能使我创建UIVIewController
Shereef Marzouk 2015年

1
这不理想,因为即使在ios8中没有默认状态栏的风景中,它也将顶部插图保持在20,
voidref

这种变化对我来说可以很好地处理任何配置:- (void)viewDidLayoutSubviews { if (self.tableView.contentInset.top != self.topLayoutGuide.length) { self.tableView.contentInset = UIEdgeInsetsMake(self.topLayoutGuide.length, 0.0, 0.0, 0.0); } }
xix

5

对于Xcode 7,取消选中导航栏的“半透明”复选标记对我来说很有效。

在此处输入图片说明


最简单,最快,最简单,最安全?解。不知道为什么与其他所有内容相比投票数很少。
Gruntcakes

3

这将为UITableViewController修复它(没有任何幻数)。我唯一无法解决的问题是正在通话中,在这种情况下,tableView的顶部被压得太紧。如果有人知道如何解决,请告诉我们。

class MyTableViewController: UITableViewController {

    override func viewDidLoad() {
        super.viewDidLoad()    
        configureTableViewTop()
    }

    override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
        super.viewWillTransitionToSize(size, withTransitionCoordinator: coordinator)
        coordinator.animateAlongsideTransition({ (context) -> Void in
            }, completion: { (context) -> Void in
                self.configureTableViewTop()
        })
    }

    func configureTableViewTop() { 
        tableView.contentInset.top = UIApplication.sharedApplication().statusBarFrame.height
    }
}

2

我不知道它是如何洁净的,但是我发现该方案将ViewController的视图下移,并为状态栏提供了坚实的背景:

- (void)viewDidLoad {
    [super viewDidLoad];

    // Shove everything down if iOS 7 or later
    float systemVersion = [[[UIDevice currentDevice] systemVersion] floatValue];
    if (systemVersion >= 7.0f) {

        // Move the view down 20 pixels
        CGRect bounds = self.view.bounds;
        bounds.origin.y -= 20.0;
        [self.view setBounds:bounds];

        // Create a solid color background for the status bar
        CGRect statusFrame = CGRectMake(0.0, -20.0, bounds.size.width, 20);
        UIView* statusBar = [[UIView alloc] initWithFrame:statusFrame];
        statusBar.backgroundColor = [UIColor redColor];
        [self.view addSubview:statusBar];
    }

当然要更换 redColor为您想要的背景颜色。

您必须单独执行其中一项操作才能在状态栏中设置字符/符号的颜色。我使用View controller-based status bar appearance = NOStatus bar style = Opaque black style在plist中,在全球范围内执行此操作。

似乎可以正常工作,我很想听到任何错误或问题。


2

当使用XIB时,chappjc的答案非常有用。

我发现以编程方式创建TableViewControllers时,最干净的解决方案是将UITableViewController实例包装在另一个UIViewController中,并相应地设置约束。

这里是:

UIViewController *containerLeftViewController = [[UIViewController alloc] init];
UITableViewController *tableViewController = [[UITableViewController alloc] init];

containerLeftViewController.view.backgroundColor = [UIColor redColor];

hostsAndMoreTableViewController.view.translatesAutoresizingMaskIntoConstraints = NO;
[containerLeftViewController.view addSubview:tableViewController.view];

[containerLeftViewController addChildViewController:tableViewController];
[tableViewController didMoveToParentViewController:containerLeftViewController];

NSDictionary * viewsDict = @{ @"tableView": tableViewController.view ,
                              @"topGuide": containerLeftViewController.topLayoutGuide,
                              @"bottomGuide": containerLeftViewController.bottomLayoutGuide,
                              };
[containerLeftViewController.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[tableView]|"
                                                                                         options:0
                                                                                         metrics:nil
                                                                                           views:viewsDict]];
[containerLeftViewController.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[topGuide][tableView][bottomGuide]"
                                                                                         options:0
                                                                                         metrics:nil
                                                                                           views:viewsDict]];

干杯本


1

我认为使用UITableViewController的方法可能与您之前所做的有所不同。它对我有用,但您可能不喜欢它。我所做的是有一个带有容器视图的视图控制器,该容器指向我的UItableViewController。这样,我便可以使用情节提要中提供给我的TopLayoutGuide。只需将约束添加到容器视图中,iOS7和iOS6都应得到照顾。


1

我最终使用了一个具有所需背景的额外视图,添加到TableView之后并置于状态栏下:

    self.CoverView = [[UIView alloc]init];

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

    self.CoverView.frame = CGRectMake(0,0,self.view.bounds.size.width,20);
    }

    self.CoverView.backgroundColor = [UIColor whiteColor];
    self.TableView = [[UITableView alloc]initWithFrame:CGRectMake(0,
    self.CoverView.bounds.size.height,XXX, YYY)];
    [self.view addSubview:self.TableView];
    [self.view addSubview:self.CoverView];

如果您需要使用无xib的视图以及IOS6和IOS7,它不是很漂亮,但是它是一个非常简单的解决方案。


1

我这样做是为了视网膜/非视网膜显示

BOOL isRetina = FALSE;

if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)]) {
    if ([[UIScreen mainScreen] scale] == 2.0) {
        isRetina = TRUE;
    } else {
        isRetina = FALSE;
    }
}

if (isRetina) {
    self.edgesForExtendedLayout=UIRectEdgeNone;
    self.extendedLayoutIncludesOpaqueBars=NO;
    self.automaticallyAdjustsScrollViewInsets=NO;
}

1

以下解决方案在不使用魔术常数的情况下就可以在代码中很好地起作用,并且说明了用户更改大小类的情况,例如通过旋转或ipad上的并排应用程序:

- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection {
    [super traitCollectionDidChange:previousTraitCollection];
    // Fix up content offset to ensure the tableview isn't underlapping the status bar.
    self.tableView.contentInset = UIEdgeInsetsMake(self.topLayoutGuide.length, 0.0, 0.0, 0.0);
}

1

适用于Swift 3-在viewDidLoad();中

let statusBarHeight = UIApplication.shared.statusBarFrame.height

let insets = UIEdgeInsets(top: statusBarHeight, left: 0, bottom: 0, right: 0)
tableView.contentInset = insets
tableView.scrollIndicatorInsets = insets

此代码:1.获取状态栏的高度。2.为表格视图的顶部提供与状态栏的高度相等的内容插图。3.为滚动指示器赋予相同的插图,使其出现在状态栏下方。


1

对于那些像我这样谁不愿意嵌入他们UITableViewControllerUIViewController尝试这个办法:

自定义UITableViewController子类可以将蒙版视图附加到tableView的超级视图。在viewDidAppear上添加遮罩,并在viewWillDisappear上删除遮罩。

private var statusBarMaskView: UIView!

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)

    if let superview = self.tableView.superview {
        self.statusBarMaskView = UIView(frame: CGRect.zero)
        superview.insertSubview(self.statusBarMaskView, aboveSubview: self.tableView)
        self.statusBarMaskView.backgroundColor = self.tableView.backgroundColor

        // using a nice constraint layout library to set the frame
        self.statusBarMaskView.pinTop(to: superview)
        self.statusBarMaskView.pinLeft(to: superview)
        self.statusBarMaskView.pinRight(to: superview)
        self.statusBarMaskView.addHeightConstraint(with: 22.0)
        ////
    }
}

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)

    self.statusBarMaskView.removeFromSuperview()
    self.statusBarMaskView = nil
}

0

我在UITableView的顶部有一个UISearchBar,以下工作了;

self.tableView.contentInset = UIEdgeInsetsMake(20, 0, 0, 0);
self.tableView.contentOffset = CGPointMake(0, -20);

分享并享受...



0

Abrahamchez的解决方案 https://developer.apple.com/library/ios/qa/qa1797/_index.html 为我工作如下。我只有一个UITableviewcontroller作为初始视图。我尝试过偏移代码并嵌入到navcon中,但都没有解决状态栏的透明度。

添加一个Viewcontroller并使其成为初始视图。这应该向您显示重要的顶部和底部布局指南。

将旧的Tableview拖到新控制器的View中。

进行所有操作以将表改装为新的控制器:

将您的旧视图controller.h文件更改为继承自UIViewController /子类,而不是UITableViewController。

将UITableViewDataSource和UITableViewDelegate添加到视图控制器的.h中。

重新连接情节提要中需要的所有内容,例如搜索栏。

最重要的是要像Apple Q&A中那样设置约束。我没有打扰插入工具栏。不确定确切的顺序。但是,也许在我构建时,“布局指南”中出现了一个红色图标。我单击它,然后让Xcode安装/清理约束。

然后,我随处单击,直到找到“垂直空间”约束并将其最大值从-20更改为0,然后它可以完美工作。


0

我正在使用带有导航控制器和tableviewcontroller的UISplitViewController。在这里尝试了许多解决方案后,这在主视图中对我有用:

float systemVersion = [[[UIDevice currentDevice] systemVersion] floatValue];
if (systemVersion >= 7.0f) {

    // Move the view down 20 pixels
    CGRect bounds = self.view.bounds;
    bounds.origin.y -= 20.0;
    [self.navigationController.view setBounds:bounds];

    // Create a solid color background for the status bar
    CGRect statusFrame = CGRectMake(0.0, -20.0, bounds.size.width, 20);
    UIView* statusBar = [[UIView alloc] initWithFrame:statusFrame];
    statusBar.backgroundColor = [UIColor whiteColor];
    [statusBar setAlpha:1.0f];
    [statusBar setOpaque:YES];
    [self.navigationController.view addSubview:statusBar];
}

它与Hot Licks的解决方案相似,但是将子视图应用于navigationController。


这是在iOS 8中对我有用的唯一答案!
SamB

0
override func viewDidLoad() {
 // your code

 if let nc = self.navigationController {
   yourView.frame.origin.y = nc.navigationBar.frame.origin.y + nc.navigationBar.frame.height
  }
}

0

这是Swift 2.3。(Xcode 8.0)解决方案。我创建了UITableView的子类。

class MYCustomTableView: UITableView
{   
    override func drawRect(rect: CGRect) {
        super.drawRect(rect)
        contentInset    = UIEdgeInsetsZero
    }
}

内容插入量应始终为零(默认情况下)。我手动将其设置为零。您还可以添加一个检查方法,该方法可以进行检查,如果它不是您想要的任何东西,则只需获取正确的矩形即可。该更改仅在绘制表视图时才会反映出来(这并不经常发生)。

不要忘记在您的IB中更新TableView(在TableViewController中或仅在ViewController中更新TableView)。


0

我在ios 11中遇到了这个问题,但是布局对于ios 8-10.3.3是正确的。就我而言,我将垂直空间约束设置为Superview.Top Margin而不是适用于ios 8-11的Superview.Top。

在此处输入图片说明


0

我发现最简单的方法是这样做,特别是如果要在选项卡栏内添加表视图,则是先添加一个视图然后在该视图内添加表视图。这为您提供了所需的顶级利润指南。

在此处输入图片说明


-1

查看所有解决方案:我的项目仅使用xib,因此,带有情节提要的解决方案不适用于我。self.edgesForExtendedLayout = UIRectEdgeNone; 如果导航栏可见,则仅适用于控制器。但是,如果您的视图仅具有状态栏,则将无法使用。所以我结合了两个条件。

- (void) viewDidLayoutSubviews {
float systemVersion = [[[UIDevice currentDevice] systemVersion] floatValue];
if (systemVersion >= 7.0f) {
    CGRect bounds = self.view.bounds;
    if(self.navigationController == nil || self.navigationController.isNavigationBarHidden == YES){
        bounds.origin.y -= 20.0;
        [self.view setBounds:bounds];
    }
    else{
        self.edgesForExtendedLayout = UIRectEdgeNone;
    }
}

帮助这项工作。


-2

如果还需要支持iOS 6,则必须有条件地将其下移。也就是说,在iOS 7中,您应该将其向下移动20个点(通过frame操纵或使用自动布局),而在iOS 6中,则不要理会它。我不相信您可以在IB中做到这一点,因此您必须在代码中做到这一点。

编辑

您实际上可以通过使用iOS6 / iOS7增量在IB中执行此操作。设置您在iOS 7中的位置,然后对于iOS 6将增量Y设置为-20点。有关更多信息,请参见此SO问题


不幸的是,将框架放下不会改变它。
Nicholas Smith

什么是行不通的?表格视图不会向下移动?
Scott Berrevoets

是的,它保持不变。我试过self.view.frameself.tableView.frame,在这两个viewWillAppearviewDidLoad。为了安全起见,我还设置setContentNeedsDisplay了强制重画。
尼古拉斯·史密斯

是否启用自动版式?
Scott Berrevoets

是的,它是自动布局和情节提要。
尼古拉斯·史密斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.