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

Ken*_*art 44 layout uiviewcontroller ios xamarin

问题

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

  • UINavigationController
    • UIViewController
      • UITableViewController

存在UIViewController影响布局.没有它,它UITableViewController占据了整个范围UINavigationController:

在此输入图像描述

但是,如果我UIViewControllerUINavigationController和之间添加一个香草,则UITableViewController在顶部UIViewController和顶部之间出现一个20px的间隙UITableViewController:

在此输入图像描述

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

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;
}
Run Code Online (Sandbox Code Playgroud)

上面仍然显示了顶部UIView和顶部之间的20px间隙UITableView.

我对这个问题的理解

我知道有些东西错误地为状态栏分配空间.使用Reveal我可以看到FrameUITableViewController具有Y的价值20.

我试过的事情

不成功

  • 设置WantsFullScreenLayouttrueUIViewController,UITableViewController和两个
  • 与播放EdgesForExtendedLayout,并ExtendedLayoutIncludesOpaqueBars同时为UIViewControllerUITableViewController
  • AutomaticallyAdjustsScrollViewInsets上了UIViewController
  • 有玩过PreservesSuperviewLayoutMarginsUITableView
  • 试图重写PrefersStatusBarHidden并返回true在两个UIViewControllerUITableViewController

成功

压倒ViewDidLayoutSubviews我的UITableViewController:

public override void ViewDidLayoutSubviews()
{
    base.ViewDidLayoutSubviews();
    this.View.Frame = this.View.Superview.Bounds;
}
Run Code Online (Sandbox Code Playgroud)

我想知道的事情

  • 是否有一种干净(呃)的方式来实现我的目标?
  • 什么是实际负责增加20px差距?的UIViewController?的UITableViewController
  • 确保我的视图控制器在不同环境中保持可用的最佳实践是什么?据推测,最重要的是将ViewDidLayoutSubviews我的视图控制器与对视觉树中显示位置的期望结合起来.如果将它托管在控制器堆栈的更高位置,事情看起来就不对了.有没有办法避免这种耦合,从而提高可重用性?

Dan*_*sko 36

您所看到的行为根本不是一个错误,而只是您滥用将视图添加到层​​次结构中的副作用.

当您将tableView添加到视图中时,您需要告诉UIKit您希望tableView相对于其父级的大小.您有两个选项:自动布局自动调整蒙版.如果没有描述您希望视图布局的方式,UIKit只需将其弹出到层次结构上,默认实现将您的视图放在顶部布局指南下(恰好是状态栏的高度).像这样简单的东西可以解决问题:

tableVC.View.Frame = rootVC.View.Bounds
tableVC.View.Autoresizingmask = UIViewAutoresizing.FlexibleWidth
tableVC.View.TranslatesAutoresizingMaskIntoConstraints = true// always nice to explicitly use auto layout
Run Code Online (Sandbox Code Playgroud)

此行为实际上并不是独占的,UITableViewController而且也是UICollectionViewController.我相信他们的默认viewLoading实现会在状态栏下隐藏视图.如果我们使childController成为一个简单的UIViewController子类,则不会出现这种行为.不要认为这是一个bug,如果你明确声明你希望如何布置他们各自的观点,你就不会有这个问题.当然,这是容器控制器的主要功能.

你的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;
}
Run Code Online (Sandbox Code Playgroud)

迅速

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
}
Run Code Online (Sandbox Code Playgroud)

注意最近写了一篇文章,描述了配置视图以填充其超级视图的方法


Ror*_*nel 8

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

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

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

  1. 而不是使用UITableViewController,添加一个UIViewController ,然后添加一个UITableView作为孩子的UIViewController.如果你这样做,那么就没有20pt的问题.这突出表明问题出在UITableViewController班上.

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

    我尝试在原始代码中添加约束来强制UITableViewController框架到顶部,但无法使其工作.再次,这可能是由于表控制器覆盖了事物本身.

  2. 如果您使用故事板构建演示,一切正常.我相信这是因为IB本身使用容器视图来嵌入新的视图控制器.因此,如果您使用storyboard,那么apple的方式是添加一个视图,您可以设置使用约束的框架,然后UITableViewController通过嵌入Segue将视图嵌入到该视图中.

    因此,根据1),使用中间的视图似乎解决了问题,并且似乎控制中间视图似乎frame 是关键.

    我注意到你唯一可行的解​​决方法是改变框架就是答案.但是在iOS7之后,似乎没有推荐更改框架,因为它可能会与想要操纵框架的约束发生冲突.

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

恕我直言我认为选项1)似乎是最安全的方法,因为你可以完全控制布局,并且不会使用帧覆盖来对抗系统,这可能会导致你以后出现问题.选项2)只有在使用故事板时才能真正起作用.您可以尝试自己手动执行相同的操作,但是谁知道嵌入Segue中发生了什么.

编辑

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


Are*_*lko 6

添加子视图控制器时,您错过了一步 - 设置其视图的框架.

见第二步:

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

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