为什么在我的UIViewController的顶部20px的差距?

问题

考虑以下控制器层次结构:

  • UINavigationController
    • UIViewController
      • UITableViewController

UIViewController的存在影响布局。 没有它, UITableViewController将占用UINavigationController的整个界限:

在这里输入图像说明

但是,如果我在UINavigationControllerUITableViewController之间添加了一个vanilla UIViewController ,那么在UIViewController的顶端和UITableViewController的顶端之间会出现一个20px的间隔:

在这里输入图像说明

即使我把代码缩减到最简单的事情,我仍然观察到这种行为。 考虑这个应用程序委托代码:

 public override bool FinishedLaunching(UIApplication app, NSDictionary options) { window = new UIWindow(UIScreen.MainScreen.Bounds); var tableView = new UITableViewController(); var intermediateView = new UIViewController(); var navigation = new UINavigationController(intermediateView); navigation.View.BackgroundColor = UIColor.Red; intermediateView.View.BackgroundColor = UIColor.Green; tableView.View.BackgroundColor = UIColor.Blue; intermediateView.AddChildViewController(tableView); intermediateView.View.AddSubview(tableView.View); tableView.DidMoveToParentViewController(intermediateView); window.RootViewController = navigation; window.MakeKeyAndVisible(); return true; } 

上面仍然显示UIView的顶部和UITableView的顶部之间20px的差距。

我对这个问题的理解

我知道有些东西是错误地分配状态栏的空间。 使用显示我可以看到UITableViewControllerFrame有一个20Y值。

我试过的东西

不成功

  • UIViewControllerUITableViewController和这两者上设置WantsFullScreenLayouttrue
  • 对于UIViewControllerUITableViewController都使用EdgesForExtendedLayoutExtendedLayoutIncludesOpaqueBars
  • 使用UIViewController上的AutomaticallyAdjustsScrollViewInsets
  • UITableView使用了PreservesSuperviewLayoutMargins
  • 尝试覆盖PrefersStatusBarHidden并在UIViewControllerUITableViewController都返回true

成功

在我的UITableViewController重写ViewDidLayoutSubviews

 public override void ViewDidLayoutSubviews() { base.ViewDidLayoutSubviews(); this.View.Frame = this.View.Superview.Bounds; } 

我想知道的事情

  • 有没有一个干净的(呃)实现我的目标的方式?
  • 什么是实际负责添加20px的差距? UIViewControllerUITableViewController
  • 什么是确保我的视图控制器在不同环境下可用的最佳实践? 据推测重写ViewDidLayoutSubviews夫妇我的视图控制器,以期望它将在视觉树中显示。 如果它被托pipe在控制器堆栈的上方,事情看起来不正确。 有没有办法避免这种耦合,从而提高可重用性?

你所看到的行为根本不是一个错误,而仅仅是错误地将视图添加到层​​次结构中的副作用。

当你将tableView添加到视图中时,你需要告诉UIKit你希望tableView相对于它的父级的大小。 您有两个选项: 自动布局或自动调整遮罩。 没有描述你希望你的视图如何布局UIKit只是简单地将其popup到层次结构中,而默认的实现会将你的视图置于顶层布局指南(恰好是状态栏的高度)。 像这样简单的事情可以做到这一点:

 tableVC.View.Frame = rootVC.View.Bounds tableVC.View.Autoresizingmask = UIViewAutoresizing.FlexibleWidth tableVC.View.TranslatesAutoresizingMaskIntoConstraints = true// always nice to explicitly use auto layout 

这种行为实际上并不是UITableViewController独有的,也是UICollectionViewController 。 我相信他们的默认viewLoading实现插入状态栏下的视图。 如果我们使childController成为一个简单的UIViewController子类,则不会显示此行为。 不要把这看作是一个错误,如果你明确地声明你希望如何布置它们各自的视图,你将不会遇到这个问题。 当然,这是容器控制器的主要function。

下面是你的appDelegate应该是什么样的:

Xamarin

 public override bool FinishedLaunching(UIApplication app, NSDictionary options) { window = new UIWindow(UIScreen.MainScreen.Bounds); var tableVC = new UITableViewController(); var rootVC = new UIViewController(); var navigationVC = new UINavigationController(intermediateView); navigationVC.View.BackgroundColor = UIColor.Red; rootVC.View.BackgroundColor = UIColor.Green; tableVC.View.BackgroundColor = UIColor.Blue; rootVC.AddChildViewController(tableVC); rootVC.View.AddSubview(tableVC.View); //YOU NEED TO CONFIGURE THE VIEWS FRAME //If you comment this out you will see the green view under the status bar tableVC.View.Frame = rootVC.View.Bounds tableVC.View.Autoresizingmask = UIViewAutoresizing.FlexibleWidth tableVC.View.TranslatesAutoresizingMaskIntoConstraints = true tableVC.DidMoveToParentViewController(rootVC); window.RootViewController = navigationVC; window.MakeKeyAndVisible(); return true; } 

迅速

 var window: UIWindow? var navigationControlller: UINavigationController! func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { let rootVC = UIViewController(nibName: nil, bundle: nil) let tableVC = UITableViewController(style: .Plain) let navVC = UINavigationController(rootViewController: rootVC) navVC.navigationBarHidden = true navVC.view.backgroundColor = UIColor.redColor() rootVC.view.backgroundColor = UIColor.greenColor() rootVC.view.addSubview(tableVC.view) //YOU NEED TO CONFIGURE THE VIEWS FRAME //If you comment this out you will see the green view under the status bar tableVC.view.frame = rootVC.view.bounds tableVC.view.autoresizingMask = .FlexibleWidth | .FlexibleHeight tableVC.view.setTranslatesAutoresizingMaskIntoConstraints(true) rootVC.addChildViewController(tableVC) tableVC.didMoveToParentViewController(rootVC) window = UIWindow(frame: UIScreen.mainScreen().bounds) window?.rootViewController = navVC window?.makeKeyAndVisible() return true } 

注意我最近写了一篇文章,描述了如何configuration一个视图来填充它的超级视图

你的问题似乎与UITableViewController被直接添加为另一个子控制器的某些基本问题有关。

环顾这不是一个新问题: iOS 7:UITableView显示在状态栏下

相对于那篇文章,我把你的代码,并尝试以下选项(一旦我意识到它是在C#8 ^)):

  1. 而不是使用UITableViewController ,添加一个UIViewController ,然后添加一个UITableView作为UIViewController一个孩子。 如果你这样做,那么没有20点问题。 这强调了问题是与UITableViewController类。

    这种方法是一个相对干净的解决方法,只有几个额外的步骤,你已经。

    我曾尝试在原始代码中添加约束,以强制UITableViewController框架顶部,但不能得到这个工作。 这又可以归结为表格控制器覆盖事物本身。

  2. 如果使用故事板构build演示,则一切正常。 我相信这是因为IB本身使用容器视图来embedded新的视图控制器。 所以,如果你使用故事板,苹果的方式是添加一个视图,你可以设置使用约束框架,然后通过embeddedSegueembeddedUITableViewController视图。

    因此,按照1),使用中间的视图似乎解决了这个问题,似乎控制中间视图frame是关键。

    我注意到你唯一可行的解​​决办法,改变框架就是答案。 然而,在iOS7后,更改框架似乎不被推荐,因为它可能与约束也冲突的问题也想操纵框架。

  3. 尝试像edgesForExtendedLayout其他选项似乎都失败了。 这些似乎是容器视图控制器的提示,而UITableViewController忽略它们。

恕我直言,我认为选项1)似乎是最安全的方法,因为你有布局的全面控制,而不是与框架覆盖,这可能会导致你后来的问题与系统。 选项2)只有在使用情节串联板的情况下才有效。 你可以尝试手动做同样的事情,但是谁知道embeddedSegue中发生了什么。

编辑

Arkadiusz Holko在回答中突出显示了一个缺失的步骤,并且为表格视图明确设置框架确实解决了问题。

添加子视图控制器时,您错过了一个步骤 – 设置其视图的框架。

请参阅第二步:

 - (void) displayContentController: (UIViewController*) content; { [self addChildViewController:content]; // 1 content.view.frame = [self frameForContentController]; // 2 [self.view addSubview:self.currentClientView]; [content didMoveToParentViewController:self]; // 3 } 

来源: https : //developer.apple.com/library/ios/featuredarticles/ViewControllerPGforiPhoneOS/CreatingCustomContainerViewControllers/CreatingCustomContainerViewControllers.html

状态栏占用20个像素。 如果您正在使用自动布局的xib文件或故事板,则可以将顶部约束设置为顶部布局指南,以便处理20像素差异

有没有一个干净(呃)的方式来实现我的目标?

只是改变框架。

 tableView.View.Frame = intermediateView.View.Bounds; tableView.View.AutoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; 

什么是实际负责添加20px的差距? UIViewController? UITableViewController?

在新build一个UIViewController后,你可以loggingUIViewController 。 你可以看到UITableViewController.view.frame总是有20px的空隙。 为什么? 我认为苹果只是初始化与20px顶部填充的屏幕大小的UITableViewController.view

确保我的视图控制器在不同的上下文中保持可用的最佳实践是什么?

如果你想添加一个UIViewController.view到另一个UIViewController.view 。最好的方法是使用故事板和使用Container View

如果你不想或不能使用故事板。 我build议只是子类UIViewaddChildViewController有时会有生命周期和布局的烦人问题。