iOS自定义View 控件自动计算size能力

iOS自定义View 控件自动计算size能力背景在使用 UILabel 和 UIImage 的时候,不用指定宽高约束,控件也不会报约束缺失,还可以根据内容自己确定适合的宽高,特别适合 Xib 和 Storyboard 布局。我们在自定义控件的...

iOS自定义View 控件自动计算size能力

背景

在使用 UILabel 和 UIImage 的时候,不用指定宽高约束,控件也不会报约束缺失,还可以根据内容自己确定适合的宽高,特别适合 Xib 和 Storyboard 布局。

我们在自定义控件的时候,怎么才能让控件具备这种自动计算宽高的功能了?

基本知识点

只需要自定义控件满足以下条件之一即可:

  1. 自定义控件实现intrinsicContentSize的重写并返回合适大小,适合子控件存在 frame初始化,或者动态添加。
  2. 自定义控件内部约束满足

这时候有部分长得帅的小伙伴要问了,如果自定义控件即满足了所有约束,又重写了intrinsicContentSize返回了计算大小怎么办。答案只有一个,执行约束,忽略 这时候有部分长得帅的小伙伴要问了,如果自定义控件即满足了所有约束,又重写了intrinsicContentSize返回了计算大小怎么办。答案只有一个,执行约束,忽略 intrinsicContentSize.

intrinsicContentSize

intrinsicContentSize又叫内容大小,决定了控件自动布局下的大小,(当控件没有宽高约束的时候,系统会自动添加宽高约束)自定义控件重写 intrisicContentsize 返回内在大小,自动布局系统就可以自己确定控件大小,就像使用UILabel,根据文本多少弹性控制大小。

自定义控件如果要适配AutoLayout, 达到自动适应的效果,则需要重写此属性。

 override var intrinsicContentSize: CGSize {
        // 计算内容大小, 如果某个维度不使用内在大小可以设置为 UIView.noIntrinsicMetric
        //return CGSize(width: bounds.width, height: itemHeight*CGFloat(items.count))
        //返回计算的size
    }

UIView.noIntrinsicMetric

内在大小需要返回 CGSize, 就是你希望控件的内在大小。

但是可能存在,我的控件只想指定高度,宽度随外部约束。这时候就需要使用UIView.noIntrinsicMetric使用-1也可以,同样的效果。

比如 UIView 的内在大小默认值等于(UIView.noIntrinsicMetric,UIView.noIntrinsicMetric)也就是(-1,-1),表示宽高由外部控件外部约束决定,或者有创建的自动约束决定。

一旦重写并返回非noIntrinsicMetric, 那么控件自己就会为自己添加对应的约束。

比如我重写

 override var intrinsicContentSize: CGSize {
        return CGSize(width: UIView.noIntrinsicMetric, height: itemH)
    }

打印约束自动添加

<NSContentSizeLayoutConstraint:0x600003650960 AnyTest.SequenceView:0x7fc91bf0a1b0.height == 162.5 Hug:250 CompressionResistance:750   (active)>

invalidateIntrinsicContentSize()

该方法的作用是重新计算intrinsicContentSize,相当于原来的内置大小失效,重新生效一个新的。

func invalidateIntrinsicContentSize()

通常需要内在大小改变时候调用,如外部数据变化时。

约束调整周期

一般情况调用顺序

invlidateIntricContentSize() --> intrisicContenSize --> updateConstraints 之后会调用 layoutSubviews()。

如 UILabel 调整周期如下:

调整contentSize layoutSubviews 方法 updateConstraints 方法 初始化 intrinsicContentSize invalidateIntrinsicContentSize 方法 约束结束

虚线表示可能调用, 可能某些步骤存在重复调用。

我们需要在合适的时机去调用invalidateIntrinsicContentSize()方法以返回正确的内在大小。

比如当某个属性改变可能引起控件的内在大小改变时调用该方法,比如UILabel设置文本的时候应该会调用这个方法(猜测)。

但要注意跳出循环(可能会引起循环)。

override func layoutSubviews() {
        super.layoutSubviews()
        //if intrinsicContentSize() != bound.size {
            //计算相应的内置大小
            invalidateIntrinsicContentSize() // 重置内容大小。
        /

本文标题为:iOS自定义View 控件自动计算size能力

基础教程推荐