layoutIfNeeded行为将iOS8更改为iOS9

时间:2015-11-13 17:12:15

标签: ios ios8 autolayout ios9 nslayoutconstraint

对于使用AutoLayout的某些情况,我需要知道我的视图(最嵌套的子视图)的宽度。使用iOS 8中的AutoLayout,我可以依赖布局系统的layoutIfNeeded来布局框架并在进行此计算之前获得适当的宽度。

一个例子是这样的:

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

这不再适用于iOS 9.我确信能够计算宽度的所有约束都被设置(通常只是前导,跟踪超级视图的约束)。

我在iOS 9的发行说明中注意到了这一点,但我真的无法解释它。

  

在iOS 9中,当layoutIfNeeded被发送到视图并且满足以下所有条件时(这不常见),我们应用拟合大小约束(UILayoutPriorityFittingSizeLevel处的width / height = 0)而不是所需的大小约束(匹配当前大小所需的宽度/高度):

     
      
  1. 接收器尚未位于托管布局引擎的视图的子树中,例如窗口,视图控制器视图(除非您已在该视图上将translateAutoresizingMaskIntoConstraints设置为NO,或者创建了在其子树中具有一个项目的约束,并且它外面的一个项目),表视图单元格内容视图,等等。
  2.   
  3. 接收器的最终祖先(即顶级视图)已将“设置为”的“设置为NO”的“变换”。
  4.   
  5. 顶级视图的子视图不是UIViewController拥有的布局指南,它也将translateAutoresizingMaskIntoConstraints设置为NO。
  6.         

    在条件1下,我们从顶级视图创建一个临时布局引擎,并将子树中的所有约束添加到它。问题是我们需要添加一些约束,使布局引擎中的顶级视图的大小明确无误。旧的行为(在iOS 9之前)是我们将添加约束以将顶级视图的大小限制为条件1下的任何情况的当前界限。当您添加条件2和3时,这实际上没有意义并且可能导致不可满足的约束记录和破坏的布局。

         

    所以在iOS 9中,仅针对这种特殊情况,我们使用拟合大小约束。

         

    这意味着如果您在iOS 9中的这些条件下将layoutIfNeeded发送到视图,您必须确保您有足够的约束来为顶级视图建立大小(通常,尽管并非总是如此)接收器)或者您必须在发送layoutIfNeeded之前将临时大小限制添加到您想要的布局大小的顶级视图中,然后将其删除。

是否有其他人遇到此问题,或熟悉如何解决?

编辑:结合更多示例

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

第一个示例是带有标签数组的自定义视图。当约束更新时,我需要知道宽度,以便我知道这些标签是继续在同一条线上还是向下移动到下一条线。

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

还有一个:

在此示例中,有时会根据条件显示文本标签。如果需要显示它,我将它展开到适当的高度,约束到它的超视图的宽度(它只有它的前导和尾随超视图的插图)。如果不需要显示,我会折叠标签。

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

还有一种情况,我需要显示textField,而不是沿着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 个答案:

答案 0 :(得分:0)

我最终覆盖了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;
}