我们最终找到了一个完整而正确的解决方案,它适用于所有情况,包括堆栈视图、动态单元格、动态行数、集合视图、动画填充、每个字符计数和所有其他情况。
填充一个UILabel,完整的解决方案。更新至2021年。
事实证明,有三件事是必须做的。
1. 必须调用textrect# forBounds与新的更小的大小
2. 必须重写drawText与新的更小的大小
3.如果是动态大小的单元格,必须调整intrinsicContentSize
在下面的典型示例中,文本单元位于表视图、堆栈视图或类似结构中,这给了它一个固定的宽度。在这个例子中,我们想要填充为60,20,20,24。
因此,我们使用“现有的”intrinsicContentSize并在高度上实际添加80。
重复一遍……
你必须从字面上“获得”引擎“到目前为止”计算的高度,并更改该值。
我觉得这个过程很混乱,但是,这就是它的工作原理。对我来说,苹果应该公开一个名为“初步高度计算”之类的调用。
其次,我们必须实际使用textrect# forBounds调用与我们的新小尺寸。
因此,在textrect# forBounds中,我们首先将大小减小,然后调用super。
警报!你必须在之后打电话给管理员,而不是在之前!
如果你仔细研究本页上的所有尝试和讨论,这就是问题所在。
注意一些解决方案“似乎通常有效”。这确实是确切的原因——令人困惑的是,你必须“事后打电话给超级”,而不是之前。
如果你“以错误的顺序”调用super,它通常会工作,但对于某些特定的文本长度会失败。
下面是一个“错误地先做超级”的直观例子:
注意,60,20,20,24边距是正确的,但大小计算实际上是错误的,因为它是用textrect# forBounds中的“super first”模式完成的。
修复:
只有现在textrect# forBounds引擎才知道如何正确地进行计算:
终于!
同样,在这个例子中,UILabel是在宽度固定的典型情况下使用的。因此,在intrinsicContentSize中,我们必须“添加”我们想要的整体额外高度。(你不需要以任何方式“添加”宽度,这将是没有意义的,因为它是固定的。)
然后在textrect# forBounds中,你通过自动布局得到了“建议到目前为止”的边界,你减去你的边距,然后才再次调用textrect# forBounds引擎,也就是说在super中,这将给你一个结果。
最后,在drawText中,你当然可以在同样小的方框中绘制。
唷!
let UIEI = UIEdgeInsets(top: 60, left: 20, bottom: 20, right: 24) // as desired
override var intrinsicContentSize:CGSize {
numberOfLines = 0 // don't forget!
var s = super.intrinsicContentSize
s.height = s.height + UIEI.top + UIEI.bottom
s.width = s.width + UIEI.left + UIEI.right
return s
}
override func drawText(in rect:CGRect) {
let r = rect.inset(by: UIEI)
super.drawText(in: r)
}
override func textRect(forBounds bounds:CGRect,
limitedToNumberOfLines n:Int) -> CGRect {
let b = bounds
let tr = b.inset(by: UIEI)
let ctr = super.textRect(forBounds: tr, limitedToNumberOfLines: 0)
// that line of code MUST be LAST in this function, NOT first
return ctr
}
再一次。请注意,关于这个问题和其他“几乎”正确的QA的答案会遇到上面第一张图中的问题——“super在错误的地方”。你必须在intrinsicContentSize中强制size更大,然后在textrect# forBounds中,你必须首先缩小第一个建议的边界,然后调用super。
总结:你必须在textrect# forBounds中“调用super last”
这就是秘诀。
请注意,您不需要也不应该需要额外调用invalidate、sizeThatFits、needsLayout或任何其他强制调用。正确的解决方案应该在正常的自动布局绘制周期中正常工作。