我们终于想出了一个完整且正确的解决方案,它适用于所有情况,包括堆栈视图、动态单元格、动态行数、集合视图、动画填充、每个字符计数以及所有其他情况。
填充UILabel,完整解决方案。 2021 年更新。
事实证明,三件事是必须要做的。
1。必须使用新的较小尺寸调用 textRect#forBounds
2。必须用新的较小尺寸覆盖 drawText
3。如果是动态大小的单元格,必须调整intrinsicContentSize
在下面的典型示例中,文本单元位于表格视图、堆栈视图或类似结构中,这使其具有固定宽度。在示例中,我们希望填充 60,20,20,24。
因此,我们采用“现有的”intrinsicContentSize实际上将高度加 80。
重复...
您必须从字面上“获取”引擎“到目前为止”计算的高度,然后更改该值。
我发现这个过程令人困惑,但是,它就是这样工作的。对我来说,Apple 应该公开一个名为“初步高度计算”之类的调用。
其次,我们必须实际使用 textRect#forBounds 调用我们新的更小的尺寸。
所以在 textRect#forBounds 中,我们首先缩小尺寸,然后调用 super。
警告!你必须在之后调用super,而不是之前!
如果您仔细调查此页面上的所有尝试和讨论,那就是确切的问题。
请注意一些解决方案“似乎通常有效”。这确实是确切的原因 - 令人困惑的是,您必须“事后调用 super”,而不是之前。
如果您“以错误的顺序”调用 super,它通常可以工作,但对于某些特定的文本长度会失败。
这是一个“错误地先做超级”的直观示例:
注意 60,20,20,24 的边距是正确的但大小计算实际上是错误的,因为它是使用 textRect#forBounds 中的“超级优先”模式完成的。
固定:
直到现在 textRect#forBounds 引擎知道如何正确地进行计算:
终于!
同样,在本例中,UILabel 用于宽度固定的典型情况。所以在 intrinsicContentSize 中,我们必须“添加”我们想要的整体额外高度。 (您不需要以任何方式“添加”宽度,因为它是固定的,这将毫无意义。)
然后在 textRect#forBounds 中,您会通过自动布局获得“到目前为止建议的”边界,减去您的边距,然后 strong> 再次调用 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 is in the wrong place”。您必须在intrinsicContentSize 中强制增大尺寸,然后在textRect#forBounds 中必须首先缩小第一个建议范围然后 调用 super。
总结:必须在textRect#forBounds中“调用super last”
这就是秘密。
请注意,您不需要也不应该额外调用 invalidate、sizeThatFits、needsLayout 或任何其他强制调用。正确的解决方案应该在正常的自动布局绘制周期中正常工作。