对于使用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)而不是所需的大小约束(匹配当前大小所需的宽度/高度):
- 接收器尚未位于托管布局引擎的视图的子树中,例如窗口,视图控制器视图(除非您已在该视图上将translateAutoresizingMaskIntoConstraints设置为NO,或者创建了在其子树中具有一个项目的约束,并且它外面的一个项目),表视图单元格内容视图,等等。
- 接收器的最终祖先(即顶级视图)已将“设置为”的“设置为NO”的“变换”。
- 顶级视图的子视图不是UIViewController拥有的布局指南,它也将translateAutoresizingMaskIntoConstraints设置为NO。
醇>在条件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];
}
答案 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;
}