67

我问这个(不知何故)简单的问题只是为了挑剔,因为有时我担心我可能会滥用许多 UIView 的 API,尤其是在自动布局方面。

为了让它变得超级简单,我将举一个例子,假设我需要一个 UIView 子类,它有一个图像图标和一个多行标签;我想要的行为是我的视图的高度随着标签的高度而变化(以适应里面的文本),另外,我正在使用界面生成器进行布局,所以我有这样的东西:

简单的视图图像

有一些约束给图像视图提供了固定的宽度和高度,并且给标签提供了固定的宽度和位置(相对于图像视图):

简单视图图像,显示约束

现在,如果我为标签设置一些文本,我希望调整视图的高度以正确适应它,或者保持与 xib 中相同的高度。在自动布局之前,我总是会做这样的事情:

在 CustoView 子类文件中,我会sizeThatFits:像这样覆盖:

- (CGSize) sizeThatFits:(CGSize)size{

    //this stands for whichever method I would have used
    //to calculate the height needed to display the text based on the font
    CGSize labelSize = [self.titleLabel intrinsicContentSize];

    //check if we're bigger than what's in ib, otherwise resize
    CGFloat newHeight = (labelSize.height <= 21) ? 51: labelSize.height+20;

    size.height = newHeight;

    return size;

}

而不是我会称之为:

myView.titleLabel.text = @"a big text to display that should be more than a line";
[myView sizeToFit];

现在,考虑到约束,我知道自动布局系统调用intrinsicContentSize视图树元素来了解它们的大小并进行计算,因此我应该intrinsicContentSize在我的子视图中重写以返回它在前面显示的方法中返回的完全相同的东西sizeThatFits:,除了事实上,以前,在调用时,sizeToFit我已经正确调整了视图的大小,但现在使用自动布局,结合 xib,这不会发生。

当然,我可能会在sizeToFit每次编辑子类中的文本时调用,以及intrinsicContentSize返回完全相同大小的覆盖sizeThatFits:,但不知何故,我认为这不是正确的做法。

我正在考虑覆盖needsUpdateConstraintsand updateConstraints,但仍然没有多大意义,因为我的视图的宽度和高度是从 xib 的自动调整掩码推断和翻译的。

这么久了,您认为最干净、最正确的方法是什么,可以准确地制作我在这里展示的内容并支持完全自动布局?

4

1 回答 1

82

I don't think you need to define an intrinsicContentSize.

Here's two reasons to think that:

  1. When the Auto Layout documentation discusses intrinsicContentSize, it refers to it as relevant to "leaf-views" like buttons or labels where a size can be computed purely based on their content. The idea is that they are the leafs in the view hierarchy tree, not branches, because they are not composed of other views.

  2. IntrinsicContentSize is not really a "fundamental" concept in Auto Layout. The fundamental concepts are just constraints and the attributes bound by constraints. The intrinsicContentSize, the content-hugging priorities, and the compression-resistance priorities are really just conveniences to be used to generate internal constraints concerning size. The final size is just the result of those constraints interacting with all other constraints in the usual way.

So what? So if your "custom view" is really just an assembly of a couple other views, then you don't need to define an intrinsicContentSize. You can just define the constraints that create the layout you want, and those constraints will also produce the size you want.

In the particular case that you describe, I'd set a >=0 bottom space constraint from the label to the superview, another one from the image to the superview, and then also a low priority constraint of height zero for the view as a whole. The low priority constraint will try to shrink the assembly, while the other constraints stop it from shrinking so far that it clips its subviews.

If you never define the intrinsicContentSize explicitly, how do you see the size resulting from these constraints? One way is to force layout and then observe the results.

Another way is to use systemLayoutSizeFittingSize: (and in iOS8, the little-heralded systemLayoutSizeFittingSize:withHorizontalFittingPriority:verticalFittingPriority:). This is a closer cousin to sizeThatFits: than is intrinsicContentSize. It's what the system will use to calculate your view's appropriate size, taking into account all constraints it contains, including intrinsic content size constraints as well as all the others.

Unfortunately, if you have a multi-line label, you'll likely also need to configure preferredMaxLayoutWidth to get a good result, but that's another story...

于 2014-10-14T05:06:44.927 回答