如何像UILabel一样创建一个具有intrinsicContentSize的视图?

时间:2017-02-20 10:42:44

标签: ios autolayout uilabel

我想创建一个自定义内容的自定义视图。

例如,我为UILabel设置了水平约束,然后它会自动换行。

我想知道系统在布局之前如何知道标签的宽度?

我打印updateConstraintslayoutSubviewsintrinsicContentSize方法,试图了解一切如何。

首先,我设置numberOfLine = 1,所以日志就像这样:

TagLabel will updateConstraints
TagLabel intrinsicContentSize 447.5 20.5
TagLabel did updateConstraints
TableViewCell will updateConstraints
TableViewCell did updateConstraints
TableViewCell will layoutSubviews
TableViewCell did layoutSubviews
TagLabel will layoutSubviews
TagLabel did layoutSubviews
TagLabel will layoutSubviews
TagLabel did layoutSubviews

然后我设置numberOfLine = 0,日志改变如下:

TagLabel will updateConstraints
TagLabel intrinsicContentSize 428.0 20.5
TagLabel did updateConstraints
TableViewCell will updateConstraints
TableViewCell did updateConstraints

// extra calls
TagLabel will updateConstraints
TagLabel intrinsicContentSize 428.0 20.5
TagLabel did updateConstraints
TagLabel intrinsicContentSize 428.0 20.5
TagLabel intrinsicContentSize 371.0 41.0

TableViewCell will layoutSubviews
TableViewCell did layoutSubviews

// extra calls
TagLabel will updateConstraints
TagLabel intrinsicContentSize 428.0 20.5
TagLabel did updateConstraints
TagLabel will updateConstraints
TagLabel intrinsicContentSize 428.0 20.5
TagLabel did updateConstraints
TagLabel will updateConstraints
TagLabel intrinsicContentSize 371.0 41.0
TagLabel did updateConstraints
TagLabel will updateConstraints
TagLabel intrinsicContentSize 428.0 20.5
TagLabel did updateConstraints
TagLabel will updateConstraints
TagLabel intrinsicContentSize 371.0 41.0
TagLabel did updateConstraints

TagLabel will layoutSubviews
TagLabel did layoutSubviews
TagLabel will layoutSubviews
TagLabel did layoutSubviews

我发现系统在layoutSubviews之前正确计算了标签的高度。

那么系统如何知道标签的宽度,以便它可以计算layoutSubviews之前的高度?

理解。

3 个答案:

答案 0 :(得分:0)

我认为[view layoutIfNeeded]可以解决您的问题。

方法layoutIfNeeded是一个同步调用,告诉系统您想要一个视图及其子视图的布局和重绘,并且您希望它立即完成,而无需等待更新周期。

答案 1 :(得分:0)

首先,您需要明确是否要创建新的UIView或子类UILabel

您可以使用大量模板代码,而无需了解系统如何处理计算。我进一步假设您想从groud中制作自定义视图。

对于此解释,我们将使用UILabel作为新自定义视图的示例和指南。

您需要了解的是,通过设置numberOfLines属性,您可以更改UILabel内的计算方式。您可以通过单击测试项目中的NOL按钮来测试它。

创建自定义视图时,您可以使用多种方法来计算自定义视图子类中自定义视图的大小。要查看此内容,请转到EXCustomLabelV并查看:

override func textRect(forBounds bounds: CGRect, limitedToNumberOfLines numberOfLines: Int) -> CGRect {

控制台中的方法输出。您将看到第一个约束更改。该值将变为 566.667 ,而高度仍然 24 。结果是添加了文本截断的“...”。但是,如果将numberOfLines属性设置为0,则约束将更改为 195.333 宽度和 71.6667 高度。

这里发生了重要的事情。系统将始终使用常量约束来计算自定义视图大小/位置。但是,在根据“外部”约束和“内部”设置计算您的子视图(自定义视图)时,您的意愿必须进行调整。

例如,EXCustomLabelV的宽度始终为 200 。无论。但是,内部textRect将不具有该大小。它有时会有 200 ,有时超过 200 ,有时甚至更少。这些计算受numberOfLines的影响。我告诉你这是因为你可能在自定义视图中有一个图像,或者有两个带有姓氏和姓氏的文本字段。

系统如何知道布局前的标签宽度? 它计算它。它首先通过检查宽度的最后一个约束(如果有的话)来询问“around”。它询问子视图(您的自定义类)的约束,然后进行计算。约束是规则。 AutoLayout会在计算时检查这些规则。

系统如何知道标签高度? 它计算它。它首先检查UILabel numberOfLines

在自定义视图中,您应该有自定义计算方法。在测试项目中,您可以看到:

override func draw(_ rect: CGRect)

总是被称为最后一个。完成所有计算后,您将获得视图的最终大小。您将始终看到您的视图宽度将等于最后一个宽度约束。

随意使用我的测试项目进行自己的计算,并了解UILabel无手的视图大小计算。

创建自定义视图时,您必须自己进行这些计算并手动更新大小和约束。然后,系统应该能够轻松更新您的视图。我建议您尝试自己计算视图的帧大小。当你经历这种“痛苦”时,你会明白AutoLayout为你做了多少。

Test project repo link

答案 2 :(得分:0)

老问题,但其他答案并没有真正回答核心问题。

<块引用>

那么系统如何知道标签的宽度以便计算 layoutSubviews之前的高度?

UILabel 的 intrinsicContentSize 方法最初返回一个 (width, height),它对应于所有没有换行符的完全布局的行。也就是说,它已经对文本进行了虚拟布局,以找出其大小。然后它返回一个宽度等于最长行的大小。

但是如果为宽度小于最长行的 UILabel 设置了一个大小——这样就需要换行——UILabel 会调用它的 invalidateIntrinsicContentSize 方法。这向布局系统表明它需要进行第二遍。在第二遍中,intrinsicContentSize 返回一个适合给定宽度和带有换行符的文本的大小。

如您在日志中所见,可能需要多次布局才能获得正确的大小和布局,尤其是当您有多个带有换行符的 UILabel 时。