We are curious about what caused the mismatch between UILabel
rendering and NSLayoutManager
calculation with different lineBreakMode
. Can we say that NSLayoutManager
doesn't support lineBreakMode
except .byWordWrapping
?
First, we have a function that creates an attributed string. In this function, we assign .byTruncatingTail
to the paragraphStyle lineBreakMode
.
func createAttributedString(with text: String) -> NSMutableAttributedString {
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineBreakMode = .byTruncatingTail
let attributedString = NSMutableAttributedString(string: text,
attributes: [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 35),
NSAttributedString.Key.paragraphStyle: paragraphStyle])
return attributedString
}
If we create a label with the following settings:
let text = (1..<20).reduce("", { "\($0)" + "\($1)-"})
let attributedString = createAttributedString(with: text)
let label = UILabel(frame: CGRect(origin: .zero, size: CGSize(width: 100, height: 500)))
label.numberOfLines = 0
label.lineBreakMode = .byTruncatingTail
label.attributedText = attributedString
we get the result:
If we use the same attributed string maker function and use NSLayoutManager
to calculate the height for a certain width with the following code reference from Apple's documentation:
let text = (1..<20).reduce("", { "\($0)" + "\($1)-"})
let attributedString = createAttributedString(with: text)
let textContainer = NSTextContainer(size: CGSize(width: 100, height: CGFloat.greatestFiniteMagnitude))
let layoutManager = NSLayoutManager()
let textStorage = NSTextStorage(attributedString: attributedString)
layoutManager.addTextContainer(textContainer)
textStorage.addLayoutManager(layoutManager)
textContainer.lineFragmentPadding = 0.0
textContainer.maximumNumberOfLines = 0
textContainer.lineBreakMode = .byTruncatingTail
layoutManager.ensureLayout(for: textContainer)
layoutManager.glyphRange(for: textContainer)
let textFrame = layoutManager.usedRect(for: textContainer)
print("textSize: \(textFrame.size)")
we get the print result:
If we assign the calculated rect to the label, we get:
This result does not match the initial label we created.
If we change the lineBreakMode
in the attributed string maker to the default value, .byWordWrapping
, we can get the result for multiple lines, which has the same height as the initial label.
If we assign the calculated rect to the label, we get:
We are curious about what caused the mismatch between UILabel
rendering and NSLayoutManager
calculation with different lineBreakMode
. Can we say that NSLayoutManager
doesn't support lineBreakMode
except .byWordWrapping
?
We are also curious about the design thought behind having the default lineBreakMode
for UILabel
be .byTruncatingTail
and for NSMutableParagraphStyle
be .byWordWrapping
.