我的应用程序的第一个屏幕是没有导航栏的 UITableViewController
,这意味着内容在状态栏下方流动,因此有很多文本冲突。我已经调整了 Under top bars
和 Adjust scroll view insets
的属性,它们实际上阻止了它向下滚动,但代价是保持表格视图的顶部在下面。我尝试将 UITableView
框架设置为偏移 20 像素,但它似乎没有生效,因为我目前需要应用程序与 iOS 6 兼容,所以我无法跳转到 iOS 7 Storyboards 强制自动布局以使用顶部高度指南。有没有人找到适用于两个版本的解决方案?
我尝试过的事情:设置 edgesForExtendedLayout
,在 Storyboard 中更改 Under top bars
和 Adjust scroll view
的设置,强制框架到一个新区域。
https://i.stack.imgur.com/6JcDv.png
UITableView
标题,它也在状态栏下方流动。
对于有兴趣复制此内容的任何人,只需按照以下步骤操作:
创建一个新的 iOS 项目 打开主故事板并删除默认/初始 UIViewController 从对象库中拖出一个新的 UITableViewController 将其设置为初始视图控制器 为表格提供一些测试数据
如果您按照上述步骤操作,当您运行该应用程序时,您将看到什么都没有,包括将 Xcode 的复选框调整为“在 {Top,Bottom,Opaque} Bars 下扩展边缘”可以阻止第一行出现在状态栏下,你也不能以编程方式解决这个问题。
例如,在上述场景中,以下内容将不起作用:
// These do not work
self.edgesForExtendedLayout=UIRectEdgeNone;
self.extendedLayoutIncludesOpaqueBars=NO;
self.automaticallyAdjustsScrollViewInsets=NO;
这个问题可能非常令人沮丧,我相信这是 Apple 的一个错误,特别是因为它出现在他们自己的对象库中预先连接的 UITableViewController
中。
我不同意所有试图通过使用任何形式的“幻数”来解决这个问题的人,例如“使用 20 像素的增量”。这种紧耦合的编程绝对不是苹果希望我们在这里做的。
我发现了解决此问题的两种方法:
保留 UITableViewController 的场景:如果您想将 UITableViewController 保留在情节提要中,而无需手动将其放置到另一个视图中,您可以将 UITableViewController 嵌入 UINavigationController(编辑器 > 嵌入 > 导航控制器)并取消选中“显示导航栏”检查员。这解决了不需要额外调整的问题,并且它还在故事板中保留了 UITableViewController 的场景。
使用 AutoLayout 并将 UITableView 嵌入到另一个视图中(我相信这就是 Apple 希望我们这样做的方式):创建一个空的 UIViewController 并将您的 UITableView 拖入其中。然后,从 UITableView 向状态栏按住 Ctrl 键拖动。当鼠标移到状态栏的底部时,您会看到一个自动布局气泡,上面写着“顶部布局指南”。释放鼠标并选择“垂直间距”。这将告诉布局系统将其放置在状态栏的正下方。
我已经在一个空的应用程序上测试了两种方式,它们都可以工作。您可能需要做一些额外的调整以使它们适用于您的项目。
如果您以编程方式执行操作并且使用没有 UINavigationController
的 UITableViewController
,那么最好的办法是在 viewDidLoad
中执行以下操作:
斯威夫特 3
self.tableView.contentInset = UIEdgeInsets(top: 20, left: 0, bottom: 0, right: 0)
早期的斯威夫特
self.tableView.contentInset = UIEdgeInsetsMake(20.0f, 0.0f, 0.0f, 0.0f);
UITableViewController
仍会滚动到状态栏后面,但滚动到顶部时不会在状态栏下方。
topLayoutGuide
长度。
UIApplication.sharedApplication.statusBarFrame.size.height
而不是 20。
请注意:这对我来说适用于以下配置:
屏幕顶部没有导航栏(表格视图遇到状态栏)
表格视图不可滚动
如果不满足上述两个要求,您的里程可能会有所不同。
原帖
我以编程方式创建了我的视图,这最终对我有用:
- (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;
}
}
Source(在第 39 页底部的 topLayoutGuide 部分)。
添加到最佳答案:
在第二种方法最初似乎不起作用之后,我做了一些额外的修补并找到了解决方案。
TLDR;最佳答案的第二个解决方案几乎可以工作,但对于某些版本的 xCode ctrl+拖动到“顶部布局指南”并选择垂直间距什么都不做。但是,通过首先调整 Table View 的大小,然后选择“Top Space to Top Layout Guide”可以工作
将一个空白的 ViewController 拖到情节提要上。将一个 UITableView 对象拖到视图中。 (不是 UITableViewController)。使用蓝色布局指南将其放置在最中心。
https://i.stack.imgur.com/1MUxA.png
将 UITableViewCell 拖到 TableView 中。这将是您的原型重用单元格,因此不要忘记在“属性”选项卡下将其设置为重用标识符,否则您会崩溃。
https://i.stack.imgur.com/bdqtv.png
创建 UIViewController 的自定义子类,并添加
https://i.stack.imgur.com/QraFO.png
右键单击 TableView 并将数据源和委托拖到您的 ViewController。
https://i.stack.imgur.com/uoxoL.png
现在对于不剪辑到状态栏的部分。
抓住表格视图的顶部边缘并将其向下移动到靠近顶部的蓝色虚线自动布局指南之一
https://i.stack.imgur.com/RmFgN.png
现在,您可以控制从 Table View 拖动到顶部并选择 Top Space to Top Layout Guide
https://i.stack.imgur.com/ECOJ6.png
它会给你一个关于 TableView 的模糊布局的错误,只需添加缺失的约束并完成。
https://i.stack.imgur.com/qsWRy.png
现在你可以像平常一样设置你的表格视图,而且它不会剪辑状态栏!
- (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;
}
}
viewDidLayoutSubviews
读取 self.topLayoutGuide.length
会搞砸子视图的布局。
在情节提要上选择 UIViewController 并取消选中 Extend Edges Under Top Bars 选项。为我工作。 :)
这是如何在“Swift”中编写它对@lipka 答案的调整:
tableView.contentInset = UIEdgeInsetsMake(20.0, 0.0, 0.0, 0.0)
- (void)viewDidLayoutSubviews { if (self.tableView.contentInset.top != self.topLayoutGuide.length) { self.tableView.contentInset = UIEdgeInsetsMake(self.topLayoutGuide.length, 0.0, 0.0, 0.0); } }
对于 Xcode 7,取消勾选导航栏的“半透明”复选标记对我有用。
https://i.stack.imgur.com/NcxZF.png
这将为 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
}
}
我不知道它是如何 Kosher 的,但我发现这个方案将 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
替换为您想要的背景颜色。
您必须单独进行其中一项调配以设置状态栏中字符/符号的颜色。我在 plist 中使用 View controller-based status bar appearance = NO
和 Status bar style = Opaque black style
来全局执行此操作。
似乎有效,我很想知道它的任何错误或问题。
chappjc 的答案在使用 XIB 时效果很好。
在以编程方式创建 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]];
干杯,本
适用于 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. 为滚动指示器提供相同的插图,因此它出现在状态栏下方。
我认为使用 UITableViewController 的方法可能与您之前所做的有点不同。它对我有用,但你可能不喜欢它。我所做的是有一个带有指向我的 UItableViewController 的容器视图的视图控制器。这样我就可以使用提供给我的故事板的 TopLayoutGuide。只需将约束添加到容器视图中,iOS7 和 iOS6 都应注意。
我最终使用了一个具有所需背景的额外视图,在 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-less 视图以及 IOS6 和 IOS7
我已经为 Retina/Non-Retina 显示器做了这个
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;
}
以下解决方案在不使用魔术常量的情况下在代码中运行良好,并考虑到用户更改大小类,例如通过 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);
}
我的 UITableView 顶部有一个 UISearchBar 并且以下工作;
self.tableView.contentInset = UIEdgeInsetsMake(20, 0, 0, 0);
self.tableView.contentOffset = CGPointMake(0, -20);
分享和享受...
我通过将大小设置为自由格式来实现它
https://i.stack.imgur.com/sJdQZ.png
Abrahamchez 的解决方案 https://developer.apple.com/library/ios/qa/qa1797/_index.html 对我有用,如下所示。我有一个 UITableviewcontroller 作为我的初始视图。我曾尝试过偏移代码并嵌入到 navcon 中,但都没有解决状态栏透明度问题。
添加一个 Viewcontroller 并使其成为初始视图。这应该向您显示关键的顶部和底部布局指南。
将旧的 Tableview 拖到新控制器的视图中。
做所有的事情来将表改装到新的控制器中:
将旧视图 controller.h 文件更改为从 UIViewController 继承/子类,而不是 UITableViewController。
将 UITableViewDataSource 和 UITableViewDelegate 添加到 viewcontroller 的 .h 中。
重新连接情节提要中所需的任何内容,例如搜索栏。
最重要的是设置约束,就像在 Apple Q&A 中一样。我没有费心插入工具栏。不确定确切的顺序。但是可能在我构建时,布局指南上出现了一个红色图标。我单击它并让 Xcode 安装/清理约束。
然后我到处点击,直到找到 Vertical Space 约束并将其顶部值从 -20 更改为 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 的解决方案,但将子视图应用于导航控制器。
override func viewDidLoad() {
// your code
if let nc = self.navigationController {
yourView.frame.origin.y = nc.navigationBar.frame.origin.y + nc.navigationBar.frame.height
}
}
这是一个 Swift 2.3。 (Xcode 8.0) 解决方案。我创建了 UITableView 的子类。
class MYCustomTableView: UITableView
{
override func drawRect(rect: CGRect) {
super.drawRect(rect)
contentInset = UIEdgeInsetsZero
}
}
内容插图应始终为零(默认情况下)。我手动将其设置为零。您还可以添加一个检查方法来进行检查,如果它不是您想要的,只需获取正确的矩形即可。更改只会在绘制 tableview 时反映出来(这不会经常发生)。
不要忘记更新 IB 中的 TableView(在 TableViewController 中或只是在 ViewController 中的 TableView)。
我在 ios 11 中遇到了这个问题,但是 ios 8 - 10.3.3 的布局是正确的。对于我的情况,我将垂直空间约束设置为 Superview.Top Margin 而不是适用于 ios 8-11 的 Superview.Top。
https://i.stack.imgur.com/VewVs.png
对于像我这样不想将 UITableViewController
嵌入 UIViewController
的人,试试这个:
自定义 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
}
我找到了执行此操作的最简单方法,特别是如果您在选项卡栏内添加表格视图是首先添加一个视图,然后在该视图中添加表格视图。这为您提供了您正在寻找的顶部边距指南。
https://i.stack.imgur.com/jBnU1.png
查看所有解决方案:我的项目只是使用 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;
}
}
帮助这个工作。
如果您还需要支持 iOS 6,则必须有条件地将其向下移动。也就是说,在 iOS 7 中,您只需将其向下移动 20 点(通过 frame
操作或使用自动布局),而在 iOS 6 中,您不必理会它。我不相信你可以在 IB 中做到这一点,所以你必须在代码中做到这一点。
编辑
您实际上可以在 IB 中通过使用 iOS6/iOS7 deltas 来执行此操作。在 iOS 7 中设置您的位置,然后在 iOS 6 中将 delta Y 设置为 -20 点。有关详细信息,请参阅 this SO question。
viewWillAppear
和 viewDidLoad
中都尝试过 self.view.frame
和 self.tableView.frame
。为了安全起见,我还设置了 setContentNeedsDisplay
来强制重绘。