Number of displayed rows for fixed width UILabel and NSAttributedString
I have a UILabel whose text is dynamically set via -setAttributedText:
NSAttributedString contains many dynamic attributes, namely different fonts or styles for different character ranges here and there throughout. UILabel width is limited (via autoplay), height is variable (numberOfLines = 0).
I need to determine the number of lines of text that are rendered given the NSAttributedString constraints and width.
Please note that I am not looking for the height of the label, I am looking for the number of lines displayed. Also note that I cannot do any calculations based on font.lineHeight because the font changes across the entire NSAttributedString.
For some background I would like to set label.textAlignment=NSTextAlignmentCenter
for labels that have 1 line and label.textAlignment=NSTextAlignmentLeft
for labels that have 2 or more lines. I would do it, for example, in -[UIViewController viewDidLayoutSubviews]
after autorun, and UILabel did its business. Or perhaps there is an easier way to achieve the same goal.
source to share
Here is Swift 3 version
extension NSAttributedString {
func numberOfLines(with width: CGFloat) -> Int {
let path = UIBezierPath(rect: CGRect(x: 0, y: 0, width: width, height: CGFloat(MAXFLOAT)))
let frameSetterRef : CTFramesetter = CTFramesetterCreateWithAttributedString(self as CFAttributedString)
let frameRef: CTFrame = CTFramesetterCreateFrame(frameSetterRef, CFRangeMake(0, 0), path.cgPath, nil)
let linesNS: NSArray = CTFrameGetLines(frameRef)
guard let lines = linesNS as? [CTLine] else { return 0 }
return lines.count
}
}
Hope it helps
source to share
This doesn't technically answer the question of how to find out the number of lines of text rendered, but I found a solution to my problem, so I thought I'd post it anyway.
What I ended up with was removing the auto-width constraint on the label (actually leading and trailing constraints for the supervisor) and adding a horizontal center to the container constraint. Then in -[UIViewController viewDidLayoutSubviews]
I installed label.preferredMaxLayoutWidth=self.view.frame.size.width-margin
. Label textAlignment=NSTextAlignmentLeft
.
This provides the same effect as the label if it is only one line and left if it is two or more.
source to share
You can install UILabel
using NSMutableAttributedString
as follows:
UILabel *contentLabel = [[UILabel alloc] initWithFrame:CGRectMake(0,0,200,100)];
[contentLabel setLineBreakMode:NSLineBreakByWordWrapping];
[contentLabel setNumberOfLines:0];
[contentLabel setFont:[UIFont systemFontOfSize:13];
NSMutableAttributedString *string = [[NSMutableAttributedString alloc] initWithString:@"hello I am a long sentence that should break over multiple lines"];
[string addAttribute:NSFontAttributeName value:[UIFont boldSystemFontOfSize:13] range:NSMakeRange(0, [string length])];
contentLabel.attributedText = string;
Hope it helps ...
source to share