ios 子视图控制器中的 topLayoutGuide

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19140530/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 01:58:11  来源:igfitidea点击:

topLayoutGuide in child view controller

iosuiviewcontrollerios7autolayoutuipageviewcontroller

提问by hpique

I have a UIPageViewControllerwith translucent status bar and navigation bar. Its topLayoutGuideis 64 pixels, as expected.

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

However, the child view controllers of the UIPageViewControllerreport a topLayoutGuideof 0 pixels, even if they're shown under the status bar and navigation bar.

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

Is this the expected behavior? If so, what's the best way to position a view of a child view controller under the real topLayoutGuide?

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

(short of using parentViewController.topLayoutGuide, which I'd consider a hack)

(缺少使用parentViewController.topLayoutGuide,我认为这是一个黑客)

采纳答案by NachoSoto

While this answermight be correct, I still found myself having to travel the containment tree up to find the right parent view controller and get what you describe as the "real topLayoutGuide". This way I can manually implement automaticallyAdjustsScrollViewInsets.

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

This is how I'm doing it:

这就是我的做法:

In my table view controller (a subclass of UIViewControlleractually), I have this:

在我的表视图控制器(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;
}

Notice the category methods in UIViewController, this is how I implemented them:

注意 中的类别方法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

Hope this helps :)

希望这可以帮助 :)

回答by chliul

you can add a constraint in the storyboard and change it in viewWillLayoutSubviews

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

something like this:

像这样:

- (void)viewWillLayoutSubviews
{
    [super viewWillLayoutSubviews];
    self.topGuideConstraint.constant = [self.parentViewController.topLayoutGuide length];
}

回答by vinaut

I might be wrong, but in my opinion the behaviour is correct. The topLayout value can be used by the container view controller to layout its view's subviews.

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

The reference says:

参考文献说:

To use a top layout guide without using constraints, obtain the guide's position relative to the top bound of the containing view.

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

In the parent, relative to the containing view, the value will be 64.

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

In the child, relative to the containing view (the parent), the value will be 0.

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

In the container View Controller you could use the property this way:

在容器视图控制器中,您可以这样使用该属性:

- (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);
    }
}

The Child view controller does not need to know that there are a Navigation and a Status bar : its parent will have already laid out its subviews taking that into account.

子视图控制器不需要知道有一个导航和一个状态栏:它的父视图已经考虑到了它的子视图。

If I create a new page based project, embed it in a navigation controller, and add this code to the parent view controllers it seems to be working fine:

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

enter image description here

在此处输入图片说明

回答by CornPuff

The documentation says to use topLayoutGuide in viewDidLayoutSubviews if you are using a UIViewController subclass, or layoutSubviews if you are using a UIView subclass.

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

If you use it in those methods you should get an appropriate non-zero value.

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

Documentation link: https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIViewController_Class/Reference/Reference.html#//apple_ref/occ/instp/UIViewController/topLayoutGuide

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

回答by Ben Sinclair

In case if you have UIPageViewControllerlike OP does and you have for example collection view controllers as children. Turns out the fix for content inset is simple and it works on iOS 8:

如果您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;
}

回答by Jesse Rusak

This has been addressed in iOS 8.

这已在 iOS 8 中得到解决。

How to set topLayoutGuide position for child view controller

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

Essentially, the container view controller should constrain the child view controller's (top|bottom|left|right)LayoutGuideas it would any other view. (In iOS 7, it was already fully constrained at a required priority, so this didn't work.)

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

回答by Glenn Maynard

I think the guides are definitely meant to be set for nested child controllers. For example, suppose you have:

我认为指南肯定是为嵌套的子控制器设置的。例如,假设您有:

  • A 100x50 screen, with a 20 pixel status bar at the top.
  • A top-level view controller, covering the whole window. Its topLayoutGuide is 20.
  • A nested view controller inside the top view covering the bottom 95 pixels, eg. 5 pixels down from the top of the screen. This view should have a topLayoutGuide of 15, since its top 15 pixels are covered by the status bar.
  • 一个 100x50 的屏幕,顶部有一个 20 像素的状态栏。
  • 一个顶层视图控制器,覆盖整个窗口。它的 topLayoutGuide 是 20。
  • 顶视图内的嵌套视图控制器覆盖底部 95 个像素,例如。从屏幕顶部向下 5 个像素。这个视图的 topLayoutGuide 应该是 15,因为它的前 15 个像素被状态栏覆盖。

That would make sense: it means that the nested view controller can set constraints to prevent unwanted overlap, just like a top-level one. It doesn't have to care that it's nested, or where on the screen its parent is displaying it, and the parent view controller doesn't need to know how the child wants to interact with the status bar.

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

That also seems to be what the documentation--or some of the documentation, at least--says:

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

The top layout guide indicates the distance, in points, between the top of a view controller's view and the bottom of the bottommost bar that overlays the view

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

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

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

That doesn't say anything about only working for top-level view controllers.

这并没有说明仅适用于顶级视图控制器。

But, I don't know if this is what actually happens. I've definitely seen child view controllers with nonzero topLayoutGuides, but I'm still figuring out the quirks. (In my case the top guide shouldbe zero, since the view isn't at the top of the screen, which is what I'm banging my head against at the moment...)

但是,我不知道这是否真的发生了。我肯定见过具有非零 topLayoutGuides 的子视图控制器,但我仍在弄清楚这些怪癖。(在我的情况下,顶部指南应该为零,因为视图不在屏幕顶部,这就是我目前正在撞到的东西......)

回答by kirander

This is the approach for the known guide length. Create constrains not to guides, but to view's top with fixed constants assuming guide distance will be.

这是已知导轨长度的方法。创建约束不是指南,但假设指南距离将是固定常数的视图顶部。

回答by Vive

Swifty implementation of @NachoSoto answer:

@NachoSoto 答案的 Swifty 实现:

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

回答by Dat Kin

Not sure if anyone still got problem with this, as I still did a few minutes ago.
My problem is like this(source gif from https://knuspermagier.de/2014-fixing-uipageviewcontrollers-top-layout-guide-problems.html).
For short, my pageViewController has 3 child viewcontrollers. First viewcontroller is fine, but when I slide to the next one, the whole view is incorrectly offset to the top (~20 pixel, I guess), but will return to normal after my finger is off the screen.
I stayed up all night looking for solution for this but still no luck finding one. Then suddenly I came up with this crazy idea:

不确定是否有人仍然对此有疑问,就像我几分钟前所做的那样。
我的问题是这样的(来自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) {

}];

My listViewControllers has 3 child viewcontrollers. The one at index 0 has problem, so I firstly set it as root of pageviewcontroller, and right after that set it back to the first view controller (as I expected). Voila, it worked!
Hope it helps!

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