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

时间:2015-05-29 08:59:59

标签: ios layout uiviewcontroller xamarin

问题

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

  • UINavigationController
    • UIViewController
      • UITableViewController

UIViewController的存在正在影响布局。没有它,UITableViewController会占用UINavigationController

的整个范围

enter image description here

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

enter image description here

即使我将代码减少到最简单的东西,我仍然会观察到这种行为。考虑一下这个app代理代码:

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; } 的顶部与UIView的顶部之间存在20px的差距。

我对问题的理解

我知道某些东西错误地为状态栏分配空间。使用显示我可以看到UITableView的{​​{1}}的{​​{1}}值为Frame

我尝试过的事情

不成功

  • UITableViewControllerY
  • 上将20设为WantsFullScreenLayout
  • trueUIViewController
  • 同时使用UITableViewControllerEdgesForExtendedLayout
  • ExtendedLayoutIncludesOpaqueBars
  • 上使用UIViewController进行了游戏
  • UITableViewController
  • 中使用AutomaticallyAdjustsScrollViewInsets
  • UIViewControllerPreservesSuperviewLayoutMargins
  • 中试图覆盖UITableView并返回PrefersStatusBarHidden

成功

在我的true覆盖UIViewController

UITableViewController

我想知道的事情

  • 是否有一种干净利落的方法来实现我的目标?
  • 实际负责增加20px的差距? ViewDidLayoutSubviewsUITableViewController
  • 确保我的视图控制器在不同环境中保持可用的最佳做法是什么?据推测,最重要的是public override void ViewDidLayoutSubviews() { base.ViewDidLayoutSubviews(); this.View.Frame = this.View.Superview.Bounds; } 将我的视图控制器与可视化树中显示的位置联系起来。如果将它托管在控制器堆栈的更高位置,事情看起来就不对了。有没有办法避免这种耦合,从而提高可重用性?

5 个答案:

答案 0 :(得分:36)

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

当您将tableView添加到视图中时,您需要告诉UIKit您希望tableView相对于其父级的大小。您有两种选择:Auto-Layout或Autoresizing Masks。如果没有描述您希望视图布局UIKit的方式,只需将其弹出到层次结构中,默认实现就会将您的视图放在顶部布局指南下(恰好是状态栏的高度)。像这样简单的东西可以解决问题:

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子类,则不会出现这种行为。不要认为这是一个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;
}

夫特

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
}

注意我写了post recently,描述了配置视图以填充其超级视图的方法

答案 1 :(得分:8)

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

展望这不是一个新问题:iOS 7: UITableView shows under status bar

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

  1. 不是使用UITableViewController,而是添加UIViewController 然后添加UITableView作为UIViewController的子级。 如果你这样做,那么就没有20pt的问题。这突出了 问题在于UITableViewController类。

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

    我确实尝试在原始代码中添加约束来强制执行 UITableViewController框架到顶部,但无法得到这个 工作。这又可以归结为表控制器的覆盖 事物本身。

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

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

    我注意到你唯一可行的解​​决方法是改变框架 答案。但是在iOS7之后,似乎没有改变框架 由于可能与之发生冲突的问题而被推荐 也想要操纵框架的约束。

  3. 尝试其他选项,例如edgesForExtendedLayout 失败。这些似乎是容器视图控制器和提示的提示 UITableViewController忽略了它们。
  4. 恕我直言,我认为选项1)似乎是最安全的方法,因为您可以完全控制布局,并且不会使用帧覆盖来对抗系统,这可能会导致您稍后出现问题。选项2)只有在使用故事板时才能真正起作用。您可以尝试自己手动执行相同的操作,但是谁知道在嵌入Segue中发生了什么。

    修改

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

答案 2 :(得分:6)

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

见第二步:

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

答案 3 :(得分:4)

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

答案 4 :(得分:2)

是否有实现目标的干净方式?

只需更改框架。

Error in clusplot.default ( x$data , x$clustering, diss = FALSE, main = main :
   argument 5 corresponds to several formal arguments

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

您可以在新tableView.View.Frame = intermediateView.View.Bounds; tableView.View.AutoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; 后记录UIViewController.view.frame。 您可以看到UIViewController总是有20px的差距。为什么?我认为Apple只是使用20px的顶部填充来初始化UITableViewController.view.frame屏幕大小。

确保我的视图控制器在不同环境中保持可用的最佳做法是什么? ...

如果您想将UITableViewController.view添加到另一个UIViewController.view。最好的方法是使用故事板并使用UIViewController.view

如果您不想或不能使用故事板。我建议只是子类Container ViewUIView有时会对生活圈和布局产生恼人的问题。