70

我有一个UIPageViewController半透明的状态栏和导航栏。topLayoutGuide正如预期的那样,它是 64 像素。

但是,UIPageViewController报告的子视图控制器 atopLayoutGuide为 0 像素,即使它们显示在状态栏和导航栏下方。

这是预期的行为吗?如果是这样,将子视图控制器的视图放置在 real 下的最佳方法是topLayoutGuide什么?

(缺少 using parentViewController.topLayoutGuide,我认为这是一种黑客行为)

4

11 回答 11

49

虽然这个答案可能是正确的,但我仍然发现自己必须遍历包含树才能找到正确的父视图控制器并获得您所描述的“真实topLayoutGuide”。这样我可以手动实现automaticallyAdjustsScrollViewInsets.

这就是我的做法:

在我的表视图控制器(UIViewController实际上是一个子类)中,我有这个:

- (void)viewWillLayoutSubviews {
    [super viewWillLayoutSubviews];

    _tableView.frame = self.view.bounds;

    const UIEdgeInsets insets = (self.automaticallyAdjustsScrollViewInsets) ? UIEdgeInsetsMake(self.ms_navigationBarTopLayoutGuide.length,
                                                                                               0.0,
                                                                                               self.ms_navigationBarBottomLayoutGuide.length,
                                                                                               0.0) : UIEdgeInsetsZero;
    _tableView.contentInset = _tableView.scrollIndicatorInsets = insets;
}

注意 中的类别方法UIViewController,这是我实现它们的方式:

@implementation UIViewController (MSLayoutSupport)

- (id<UILayoutSupport>)ms_navigationBarTopLayoutGuide {
    if (self.parentViewController &&
        ![self.parentViewController isKindOfClass:UINavigationController.class]) {
        return self.parentViewController.ms_navigationBarTopLayoutGuide;
    } else {
        return self.topLayoutGuide;
    }
}

- (id<UILayoutSupport>)ms_navigationBarBottomLayoutGuide {
    if (self.parentViewController &&
        ![self.parentViewController isKindOfClass:UINavigationController.class]) {
        return self.parentViewController.ms_navigationBarBottomLayoutGuide;
    } else {
        return self.bottomLayoutGuide;
    }
}

@end

希望这可以帮助 :)

于 2014-02-04T01:48:12.157 回答
11

我可能是错的,但在我看来,这种行为是正确的。容器视图控制器可以使用 topLayout 值来布局其视图的子视图。

参考资料说:

要在不使用约束的情况下使用顶部布局指南,请获取指南相对于包含视图的顶部边界的位置。

在父视图中,相对于包含视图,该值将为 64。

在子视图中,相对于包含视图(父视图),该值将为 0。

在容器视图控制器中,您可以通过以下方式使用该属性:

- (void) viewWillLayoutSubviews {

    CGRect viewBounds = self.view.bounds;
    CGFloat topBarOffset = self.topLayoutGuide.length;

    for (UIView *view in [self.view subviews]){
        view.frame = CGRectMake(viewBounds.origin.x, viewBounds.origin.y+topBarOffset, viewBounds.size.width, viewBounds.size.height-topBarOffset);
    }
}

Child 视图控制器不需要知道有一个 Navigation 和一个 Status bar :它的父级已经考虑到了它的子视图。

如果我创建一个新的基于页面的项目,将其嵌入导航控制器,并将此代码添加到父视图控制器,它似乎工作正常:

在此处输入图像描述

于 2013-10-11T12:00:31.783 回答
11

您可以在情节提要中添加约束并在 viewWillLayoutSubviews 中更改它

像这样的东西:

- (void)viewWillLayoutSubviews
{
    [super viewWillLayoutSubviews];
    self.topGuideConstraint.constant = [self.parentViewController.topLayoutGuide length];
}
于 2014-03-06T09:19:56.933 回答
8

文档说如果您使用 UIViewController 子类,则在 viewDidLayoutSubviews 中使用 topLayoutGuide,如果您使用 UIView 子类,则使用 layoutSubviews。

如果你在这些方法中使用它,你应该得到一个适当的非零值。

文档链接: https ://developer.apple.com/library/ios/documentation/UIKit/Reference/UIViewController_Class/Reference/Reference.html#//apple_ref/occ/instp/UIViewController/topLayoutGuide

于 2014-02-10T21:47:57.103 回答
3

如果您有UIPageViewController像 OP 一样的情况,并且您有例如集合视图控制器作为子项。结果发现内容插入的修复很简单,并且适用于 iOS 8:

- (void)viewWillLayoutSubviews {
    [super viewWillLayoutSubviews];

    UIEdgeInsets insets = self.collectionView.contentInset;
    insets.top = self.parentViewController.topLayoutGuide.length;
    self.collectionView.contentInset = insets;
    self.collectionView.scrollIndicatorInsets = insets;
}
于 2014-12-15T17:03:04.673 回答
2

这已在 iOS 8 中解决。

如何为子视图控制器设置 topLayoutGuide 位置

本质上,容器视图控制器应该像约束(top|bottom|left|right)LayoutGuide任何其他视图一样约束子视图控制器。(在 iOS 7 中,它已经完全限制在所需的优先级上,所以这不起作用。)

于 2014-07-21T18:54:52.557 回答
1

我认为这些指南绝对是为嵌套的子控制器设置的。例如,假设您有:

  • 一个 100x50 的屏幕,顶部有一个 20 像素的状态栏。
  • 一个顶层视图控制器,覆盖整个窗口。它的 topLayoutGuide 是 20。
  • 覆盖底部 95 像素的顶视图内的嵌套视图控制器,例如。从屏幕顶部向下 5 个像素。这个视图的 topLayoutGuide 应该是 15,因为它的前 15 个像素被状态栏覆盖。

这是有道理的:这意味着嵌套视图控制器可以设置约束以防止不必要的重叠,就像顶级视图控制器一样。它不必关心它是嵌套的,或者它的父级在屏幕上的哪个位置显示它,并且父级视图控制器不需要知道子级想要如何与状态栏交互。

这似乎也是文档(或至少一些文档)所说的:

顶部布局指南指示视图控制器视图的顶部和覆盖视图的最底部栏的底部之间的距离(以磅为单位)

https://developer.apple.com/library/ios/documentation/UIKit/Reference/UILayoutSupport_Protocol/Reference/Reference.html

这并没有说明只为顶级视图控制器工作。

但是,我不知道这是否真的发生了。我肯定见过带有非零 topLayoutGuides 的子视图控制器,但我仍在找出其中的怪癖。(在我的情况下,顶部指南应该为零,因为视图不在屏幕的顶部,这就是我现在正在努力解决的问题......)

于 2013-11-13T01:11:43.077 回答
0

这是已知导向长度的方法。创建约束不是对指南,而是在假设指南距离为固定常数的情况下查看顶部。

于 2014-12-17T22:43:18.270 回答
0

@NachoSoto 的快速实现答案:

extension UIViewController {

    func navigationBarTopLayoutGuide() -> UILayoutSupport {
        if let parentViewController = self.parentViewController {
            if !parentViewController.isKindOfClass(UINavigationController) {
                return parentViewController.navigationBarTopLayoutGuide()
            }
        }

        return self.topLayoutGuide
    }

    func navigationBarBottomLayoutGuide() -> UILayoutSupport {
        if let parentViewController = self.parentViewController {
            if !parentViewController.isKindOfClass(UINavigationController) {
                return parentViewController.navigationBarBottomLayoutGuide()
            }
        }

        return self.bottomLayoutGuide
    }
}
于 2015-06-23T08:43:42.417 回答
0

不知道是否还有人对此有疑问,就像我在几分钟前所做的那样。
我的问题是这样的(来自https://knuspermagier.de/2014-fixing-uipageviewcontrollers-top-layout-guide-problems.html的源 gif )。
简而言之,我的 pageViewController 有 3 个子视图控制器。第一个视图控制器很好,但是当我滑动到下一个时,整个视图错误地偏移到顶部(我猜大约 20 像素),但在我的手指离开屏幕后会恢复正常。
我熬夜寻找解决方案,但仍然没有找到一个。然后突然想到了这个疯狂的想法:

[pageViewController setViewControllers:@[listViewControllers[1]] direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:^(BOOL finished) {

}];

[pageViewController setViewControllers:@[listViewControllers[0]] direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:^(BOOL finished) {

}];

我的 listViewControllers 有 3 个子视图控制器。索引 0 的那个有问题,所以我首先将它设置为 pageviewcontroller 的根,然后立即将它设置回第一个视图控制器(如我所料)。瞧,它奏效了!
希望能帮助到你!

于 2015-11-14T03:10:24.063 回答
0

这是一个不幸的行为,在 iOS 11 中似乎已通过安全区域 API 改进得到纠正。也就是说,您将始终从根视图控制器中获得正确的值。例如,如果您想要 iOS 11 之前的安全区域高度上限:

斯威夫特 4

let root = UIApplication.shared.keyWindow!.rootViewController!
let topLayoutGuideLength = root.topLayoutGuide.length
于 2018-01-28T20:13:46.123 回答