经过几次尝试,我发现NSAttributedString
我设置byTruncatingTail
的lineBreakMode
值是 for NSParagraphStyle
(这是我们在应用程序中使用的默认值)。
为了实现所需的行为,我必须将其更改为byWordWrapping
or byCharWrapping
。
let paragraphStyle = NSMutableParagraphStyle()
// When setting "byTruncatingTail" it returns a single line height
// paragraphStyle.lineBreakMode = .byTruncatingTail
paragraphStyle.lineBreakMode = .byWordWrapping
let stringAttributes: [NSAttributedString.Key: Any] = [.font: UIFont(name: "Avenir-Book", size: 16.0)!,
.paragraphStyle: paragraphStyle]
let attributedString = NSAttributedString(string: string,
attributes: stringAttributes)
let computedSize = attributedString.boundingRect(with: CGSize(width: 200, height: CGFloat.greatestFiniteMagnitude),
options: .usesLineFragmentOrigin,
context: nil)
computedSize.height
请注意,当使用byTruncatingTail
a 上的值UILabel
(其中 numberOfLines
值为 0)设置属性字符串时,该字符串“自动”调整为多行,这在计算boundingRect
.
NSAttributedString
在计算在 a 中使用的高度时,还有其他因素需要牢记UITextView
(每一个都可能导致字符串不完全包含在文本视图中):
1. 边界改变时重新计算高度
由于高度是基于边界的,所以当边界改变时应该重新计算。这可以在bounds
keypath 上使用 KVO 来实现,当这种变化时布局无效。
observe(\.bounds) { (_, _) in
invalidateIntrinsicContentSize()
layoutIfNeeded()
}
在我的情况下intrinsicContentSize
,我的自定义无效,UITextView
因为这是我根据计算的字符串高度调整它的方式。
2.使用NSTextContainer
宽度
使用textContainer.width
(而不是bounds.width
)作为用于方法调用的固定宽度,boundingRect
因为它会考虑任何textContainerInset
值(尽管默认值为 0)left
right
textContainerInsets
3.为字符串高度添加垂直值
在计算NSAttributedString
高度之后,我们应该添加textContainerInsets.top
并textContainerInsets.bottom
计算正确的UITextField
高度(它们的默认值是 8.0...)
override var intrinsicContentSize: CGSize {
let computedHeight = attributedText.boundingHeight(forFixedWidth: textContainer.size.width)
return CGSize(width: bounds.width,
height: computedHeight + textContainerInset.top + textContainerInset.bottom)
}
4.删除lineFragmentPadding
将 0 设置为的值,lineFragmentPadding
或者,如果您想拥有它,请记住在计算NSAttributedString
高度之前将其值从“固定宽度”中删除
textView.textContainer.lineFragmentPadding = 0
5. 应用于ceil
计算高度
返回的高度值boundingRect
可以是小数,如果我们按原样使用它可能会导致最后一行不显示。将其传递给ceil
函数以获取上整数值,以避免向下舍入。