【问题标题】:Why the 20px gap at the top of my UIViewController?为什么我的 UIViewController 顶部有 20px 的间隙?
【发布时间】:2015-08-12 01:46:03
【问题描述】:

问题

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

  • UINavigationController
    • UIViewController
      • UITableViewController

UIViewController 的存在影响了布局。没有它,UITableViewController 会占据UINavigationController 的整个边界:

但是,如果我在UINavigationController 和UITableViewController 之间添加一个香草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的间隙。

我对问题的理解

我了解 something 错误地为状态栏分配空间。使用 Reveal 我可以看到UITableViewController 的Frame 的Y 值为20。

我尝试过的事情

不成功

  • 在UIViewController、UITableViewController 和两者上将WantsFullScreenLayout 设置为true
  • 在UIViewController 和UITableViewController 中使用EdgesForExtendedLayout 和ExtendedLayoutIncludesOpaqueBars
  • 在UIViewController 上使用AutomaticallyAdjustsScrollViewInsets
  • 在UITableView 中使用PreservesSuperviewLayoutMargins
  • 尝试在 UIViewController 和 UITableViewController 中覆盖 PrefersStatusBarHidden 并返回 true

成功

因此在我的UITableViewController 中覆盖ViewDidLayoutSubviews:

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

我想知道的事情

  • 有没有更干净(更好)的方式来实现我的目标?
  • 实际上是什么负责添加 20px 间隙? UIViewController? UITableViewController?
  • 确保我的视图控制器在不同的上下文中保持可用的最佳实践是什么?大概覆盖ViewDidLayoutSubviews 将我的视图控制器与预期将在可视树中显示的位置结合起来。如果它被托管在控制器堆栈的更高位置,事情看起来就不对了。有没有办法避免这种耦合,从而提高可重用性?

【问题讨论】:

  • 确保你没有半透明的导航栏
  • 还要确保您没有意外设置 tableview inset
  • @KingBabar:即使是最简单的重现也证明了这个问题。请参阅我更新的问题中的代码。我没有设置 任何东西 - 我只是在使用 vanilla 视图控制器。
  • @KingBabar:就像我说的,我使用的是代码,而不是故事板。请参阅我的问题。
  • 啊!我使用 c#,但在“iOS 模式”下我是盲目的,我使用 swift 复制了它,所以我可以排除 xamarin 的问题。虽然我还没有找到解决方案/原因

标签: ios layout uiviewcontroller xamarin


【解决方案1】:

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

当您将 tableView 添加到视图中时,您需要告诉 UIKit 您希望该 tableView 相对于其父级的大小。您有两个选择:Auto-Layout 或自动调整蒙版。在不描述您希望视图如何布局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 子类,则不会出现任何这种行为。不过不要将此视为错误,如果您明确声明希望如何布置他们各自的视图,则不会遇到此问题。当然,这是容器控制器的主要功能。

你的 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 描述了您可以配置视图以填充其父视图的方式

【讨论】:

  • 很棒的答案,而且效果很好。出于某种原因,我认为默认行为是让子视图填充其父视图。感谢您为我解决这个问题。
  • 没问题@KentBoogaart :)
【解决方案2】:

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

环顾四周,这不是一个新问题:iOS 7: UITableView shows under status bar

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

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

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

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

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

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

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

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

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

编辑

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

【讨论】:

  • 我认为 OP 根本没有使用约束,这就是他没有收到警告的原因。让视图控制自己的框架只是不好的做法。所需要的只是设置 tableViews 框架和调整掩码大小...stackoverflow.com/a/30614342/1652402
  • @sschunara 不确定您是否评论了错误的答案?你的选择就是我在这个答案中提出的。然而,真正的修复(或真正正确的代码)被发现是设置子控制器的框架。
  • @Rory McKinnel 是的,抱歉,我需要对这个问题发表评论。我从这里删除了。
【解决方案3】:

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

见第二步:

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

【讨论】:

  • 这是一个更好的解决方案,但请尝试将您的答案上下文化,而不是粘贴示例代码:)
  • 我将其粘贴为 Apple 建议如何设置子视图控制器的参考。这个问题没有使用 Objective-C 或 Swift,所以我不确定我的代码是否可以编译。
  • 唯一的问题是它没有提到任何使用自动布局或调整掩码大小。可能应该向苹果提出这个问题:)
【解决方案4】:

20 像素被状态栏占用。如果您使用带有自动布局的 xib 文件或情节提要,您可以将顶部约束设置为 顶部布局指南,以便处理 20 像素的差异

【讨论】:

  • 我正在代码中创建控制器。请参阅我更新的问题以获得最简单的重现。根本没有自定义代码 - 只是创建控制器并建立它们的关系。仍然会产生 20 像素的间隙。
【解决方案5】:

有没有更干净(更好)的方法来实现我的目标?

只需更换框架即可。

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

添加 20px 间隙的真正原因是什么? UIViewController? UITableViewController?

您可以在新UIViewController 后登录UIViewController.view.frame。 你可以看到UITableViewController.view.frame 总是有 20px 的间隙。为什么?我认为 Apple 只是将 UITableViewController.view 初始化为具有 20px 顶部填充的屏幕尺寸。

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

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

如果您不想或不能使用故事板。我建议只是子类UIView。 addChildViewController生活圈子和布局有时会遇到烦人的问题。

【讨论】:

  • 我不会说“最好的方法”是使用故事板。它可以很容易地以编程方式完成,特别是因为 OP 似乎根本没有使用故事板
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-23
  • 2016-09-25
  • 2014-10-07
  • 2019-10-26
相关资源
最近更新 更多