ios 正确使用 internalContentSize 和 sizeThatFits:在带有自动布局的 UIView 子类上

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/24127032/
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 00:43:58  来源:igfitidea点击:

Proper usage of intrinsicContentSize and sizeThatFits: on UIView Subclass with autolayout

iosobjective-cuiviewautolayoutcgsize

提问by dev_mush

I'm asking this (somehow) simple question just to be finicky, because sometimes I'm worried about a misuse I might be doing of many UIView's APIs, especially when it comes to autolayout.

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

To make it super simple I'll go with an example, let's assume I need an UIView subclass that has an image icon and a multiline label; the behaviour I want is that the height of my view changes with the height of the label (to fit the text inside), also, I'm laying it out with Interface builder, so I have something like this:

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

simple view image

简单视图图像

with some constraints that give fixed width and height to the image view, and fixed width and position (relative to the image view) to the label:

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

simple view image, constraints shown

简单视图图像,显示约束

Now, if I set some text to the label, I want the view to be resized in height to fit it properly, or remain with the same height it has in the xib. Before autolayout I would have done always something like this:

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

In the CustoView subclass file I would have overridden sizeThatFits:like so:

在 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;

}

And than I would have called something like:

而不是我会调用类似的东西:

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

Now, thinking in constraints, I know that autolayout systems calls intrinsicContentSizeon the view tree elements to know what their size is and make its calculations, therefore I should override intrinsicContentSizein my subview to return the exact same things it returns in the sizeThatFits:method previously shown, except for the fact that, previously, when calling sizeToFitI had my view properly resized, but now with autolayout, in combination with a xib, this is not going to happen.

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

Of course I might be calling sizeToFitevery time I edit text in my subclass, along with an overridden intrinsicContentSizethat returns the exact same size of sizeThatFits:, but somehow I don't think this is the proper way of doing it.

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

I was thinking about overriding needsUpdateConstraintsand updateConstraints, but still makes not much sense since my view's width and height are inferred and translated from autoresizing mask from the xib.

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

So long, what do you think is the cleanest and most correct way to make exactly what I show here and support fully autolayout?

这么长时间,您认为最干净、最正确的方法是制作我在这里展示的内容并支持完全自动布局?

回答by algal

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.

  1. 当自动布局文档讨论 时intrinsicContentSize,它将它称为与“叶视图”相关的按钮或标签,其中可以完全根据其内容计算大小。这个想法是它们是视图层次树中的叶子,而不是分支,因为它们不是由其他视图组成的。

  2. IntrinsicContentSize 在自动布局中并不是真正的“基本”概念。基本概念只是约束和受约束约束的属性。固有内容大小、内容拥抱优先级和抗压缩优先级实际上只是用于生成有关大小的内部约束的便利。最终的大小只是这些约束以通常的方式与所有其他约束交互的结果。

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 priorityconstraint 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.

在您描述的特定情况下,我会设置一个 >=0 的底部空间约束,从标签到超级视图,另一个从图像到超级视图,然后还有一个低优先级约束,高度为零的视图作为所有的。低优先级约束将尝试缩小装配,而其他约束阻止它缩小到剪裁其子视图的程度。

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.

另一种方法是使用systemLayoutSizeFittingSize:(在 iOS8 中,小先驱systemLayoutSizeFittingSize:withHorizontalFittingPriority:verticalFittingPriority:)。这是sizeThatFits:比 is更近的表亲intrinsicContentSize。系统将使用它来计算视图的适当大小,同时考虑它包含的所有约束,包括内在内容大小约束以及所有其他约束。

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

不幸的是,如果您有一个多行标签,您可能还需要进行配置preferredMaxLayoutWidth才能获得良好的结果,但那是另一回事了……