layoutifneed行为改变iOS8到iOS9。

时间:2021-03-15 23:00:37

For certain cases with AutoLayout I need to know the width of my view (most nested subview) within it's superview. With AutoLayout in iOS 8 I was able to rely on layoutIfNeeded for the layout system to layout the frames and get the proper width before I do this calculation.

对于AutoLayout的某些情况,我需要知道在它的父视图中我的视图(大多数嵌套子视图)的宽度。在ios8中AutoLayout,我可以依靠layoutif需要的布局系统来布局框架,在我做这个计算之前得到合适的宽度。

An example would be something like this:

一个例子是这样的:

- (CGSize)intrinsicContentSize {
    [self layoutIfNeeded];
    CGSize size = [self roundedSizeAccountingLeftRightInsets:CGSizeMake(self.bounds.size.width, CGFLOAT_MAX)];
    size.height += self.insets.top + self.insets.bottom;
    return size;
}

This no longer works with iOS 9. I'm sure that all constraints to be able to calculate the width are set (usually just leading, trailing constraints bound to the superview).

这不再适用于iOS 9。我确信能够计算宽度的所有约束都是设置的(通常只是引导,尾随约束绑定到父视图)。

I noticed this in the release notes for iOS 9 but I wasn't really able to interpret it.

我在iOS 9的发行版中注意到了这一点,但我并没有真正理解它。

In iOS 9, when layoutIfNeeded is sent to a view and all of the following conditions are satisfied (which is not common), we apply fitting-size constraints (width/height = 0 at UILayoutPriorityFittingSizeLevel) instead of required size constraints (width/height required to match current size):

在ios9中,当layoutifrequired被发送到一个视图,所有的条件都满足(这是不常见的),我们在UILayoutPriorityFittingSizeLevel中应用安装大小限制(width/height = 0),而不是要求的大小限制(匹配当前大小所需的宽度/高度):

  1. The receiver is not yet in the subtree of a view that hosts a layout engine, such as window, view controller view (unless you have set translatesAutoresizingMaskIntoConstraints to NO on that view—or created constraints that have one item in its subtree and one item outside it), table view cell content view, and so on.
  2. 接收方还没有在一个视图的子树,主持一个布局引擎,如窗口中,视图控制器的视图(除非你没有设置translatesAutoresizingMaskIntoConstraints视图或创建约束在子树和一项一项外),表视图单元格内容视图,等等。
  3. The final ancestor (that is, top-level view) of the receiver has translatesAutoresizingMaskIntoConstraints set to NO.
  4. 接收者的最后一个祖先(即顶层视图)具有自定义的翻译自定义。
  5. The top-level view has a subview that is not a UIViewController-owned layout guide that also has translatesAutoresizingMaskIntoConstraints set to NO.
  6. 顶层视图有一个子视图,它不是一个uiviewcontroller拥有的布局向导,它还具有将设置为NO的translatesAutoresizingMaskIntoConstraints。

Under condition 1, we create a temporary layout engine from the top-level view and add all the constraints from the subtree to it. The problem is that we need to add some constraints that make the size of the top-level view unambiguous in the layout engine. The old behavior (prior to iOS 9) was that we would add constraints to restrict the size of the top-level view to its current bounds for any situation under condition 1. This really doesn’t make sense when you add conditions 2 and 3 and can result in unsatisfiable-constraints logging and broken layout.

在条件1中,我们从顶层视图创建一个临时的布局引擎,并将子树的所有约束添加到它。问题是,我们需要添加一些约束,使顶层视图的大小在布局引擎中保持不模糊。旧的行为(在ios9之前)是,我们会添加约束来限制顶层视图的大小,以满足条件1下的任何情况。当您添加条件2和3时,这真的没有意义,并且可能导致无法满足的约束日志记录和破坏布局。

So in iOS 9, for this special case only, we use fitting-size constraints instead.

所以在ios9中,对于这个特例,我们用的是合适的尺寸约束。

This means that if you are sending layoutIfNeeded to a view under these conditions in iOS 9, you must be sure that either you have sufficient constraints to establish a size for the top-level view (which usually, though not always, is the receiver) or you must add temporary size constraints to the top-level view of layout size you desire before sending layoutIfNeeded, and remove them afterward.

这意味着,如果你发送layoutIfNeeded视图在这些条件下在iOS 9中,你必须确保你有足够的约束建立顶层视图的大小(通常,但并非总是,是接收机)或您必须添加临时尺寸约束的顶层视图布局尺寸你想要发送layoutIfNeeded之前,和之后删除它们。

Has anyone else encountered this issue, or familiar with how to solve?

有没有人遇到过这个问题,或者熟悉如何解决?

Edit: Couple More Examples

编辑:几个例子

I usually do this when I need to know explicitly what the layout width will be of the superview because constraints of the subview are dependent on this value and can't be expressed with preferredMaxLayoutWidth.

当我需要明确地知道超视图的布局宽度时,我通常会这样做,因为子视图的约束依赖于这个值,不能用preferredMaxLayoutWidth来表示。

The first example is a custom view with an array of labels. When constraints are updated I need to know the width so I can know if those labels will continue on the same line or move down to the next line.

第一个示例是带有一系列标签的自定义视图。当约束被更新时,我需要知道宽度,这样我就能知道这些标签是否会继续在同一条线上,或者移动到下一行。

- (void)updateConstraints {
    [self layoutIfNeeded];
    CGFloat width = self.view.bounds.size.width;
    for (UILabel *label in self.labels) {
    CGSize labelSize = [label sizeThatFits:CGSizeZero];
    CGFloat minLabelWidth = MAX(12, labelSize.width);
    labelSize.width = minLabelWidth;
    lineWidth += labelSize.width + 10;
    if (lineWidth >= width) {
        // update some variables to where I will actually be applying constraints 
    }
    [label mas_updateConstraints:^(MASConstraintMaker *make) {
          // constraint magic
    }];  
    [super updateConstraints];
}

One more:

一个:

In this example there will sometimes be a text label that is shown based on a condition. If it needs to be shown I expand it to it's appropriate height constrained to the width of it's superview (it only has insets to it's leading and trailing superview). If it doesn't need to be shown I collapse the label.

在这个例子中,有时会有一个基于条件的文本标签。如果需要显示,我将它扩展到它的适当高度限制到它的父视图的宽度(它只包含了它的前导和后超视图)。如果不需要显示,我就会折叠标签。

- (void)updateConstraints {
    // Need layout pass to get the proper width.
    [self layoutIfNeeded];
    CGFloat textHeight = [self.label sizeThatFits:CGSizeMake(self.bounds.size.width - 32, CGFLOAT_MAX)].height;
    [self.label mas_remakeConstraints:^(MASConstraintMaker *make) {
        // update other constraints
        make.height.equalTo( showThisText ? @(textHeight) : @0 );
    }];
    [super updateConstraints];
}

There can also be a case when I need a textField to be shown and not be pushed off the screen by other elements along the x axis so I have to give it a fixed width via constraints but I need to know the max width before I do that

也可以有一个情况我需要显示一个文本框,而不是被其他元素推出屏幕沿着x轴所以我必须给它一个固定宽度通过约束但之前我需要知道的最大宽度

- (void)updateConstraints {
    [self layoutIfNeeded];
    CGFloat textFieldWidth = self.bounds.size.width - someVariable;
    [self.textField mas_remakeConstraints:^(MASConstraintMaker *make) {
      make.width.equalTo(@(textFieldWidth));
    }];
    [super updateConstraints];
}

1 个解决方案

#1


0  

I ended up overriding layoutSubviews since this is a UIView subclass and it seems to be working now with this code

我最后重写了layoutSubviews,因为这是一个UIView子类,它现在似乎正在使用这个代码。

- (void)layoutSubviews {
    [super layoutSubviews];

    static CGSize viewBounds = { 0, 0 };
    static CGSize previousViewBounds = { 0, 0 };
    [self setNeedsUpdateConstraints];
    viewBounds = CGSizeMake(self.bounds.size.width, self.bounds.size.height);
    if (!CGSizeEqualToSize(viewBounds, previousViewBounds)) [self setNeedsUpdateConstraints];
    previousViewBounds = viewBounds;
}

#1


0  

I ended up overriding layoutSubviews since this is a UIView subclass and it seems to be working now with this code

我最后重写了layoutSubviews,因为这是一个UIView子类,它现在似乎正在使用这个代码。

- (void)layoutSubviews {
    [super layoutSubviews];

    static CGSize viewBounds = { 0, 0 };
    static CGSize previousViewBounds = { 0, 0 };
    [self setNeedsUpdateConstraints];
    viewBounds = CGSizeMake(self.bounds.size.width, self.bounds.size.height);
    if (!CGSizeEqualToSize(viewBounds, previousViewBounds)) [self setNeedsUpdateConstraints];
    previousViewBounds = viewBounds;
}