开发者

How to find UILabel's number of Lines

开发者 https://www.devze.com 2023-01-24 17:39 出处:网络
I displayed the text in UILabel by using wrap method. 开发者_如何转开发Now I want to need to find thehow many number of line is there in UILabel.

I displayed the text in UILabel by using wrap method. 开发者_如何转开发Now I want to need to find the how many number of line is there in UILabel.

If there is any possible way to find the UILabel's number of lines count.

Thanks.


As pointed out, this post relates how to get the height, rather than the number of lines. To get the number of lines,

  1. Get the height for a single letter, e.g. @"A".
  2. Divide the height of the string by the height obtained in 1 above.

E.g.

CGFloat unitHeight = [@"A" heightForWidth:width usingFont:font];
CGFloat blockHeight = [text heightForWidth:width usingFont:font];
NSInteger numberOfLines =        ceilf(blockHeight / unitHeight); 
// need to #include <math.h> for ^^^^^

As of iOS 7, the way of getting a label's desired height changed. To get the height, you can use the following code:

NSStringDrawingContext *context = [[NSStringDrawingContext alloc] init];
CGSize labelSize = (CGSize){width, FLT_MAX};
CGRect r = [self boundingRectWithSize:labelSize options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName: font} context:context];

where the height is r.size.height. Note that font must be provided. You can put this into a category for NSString for convenience, e.g.

@implementation NSString (HeightCalc)

- (CGFloat)heightForWidth:(CGFloat)width usingFont:(UIFont *)font
{
    NSStringDrawingContext *context = [[NSStringDrawingContext alloc] init];
    CGSize labelSize = (CGSize){width, FLT_MAX};
    CGRect r = [self boundingRectWithSize:labelSize options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName: font} context:context];
    return r.size.height;
}

@end

(Do memory management if not using ARC, btw.)

For iOS 6 and below:

Let's say you have a UILabel *myLabel and you want to find out the height of the label (with some tweaking, you can get the # of lines by dividing the height with some appropriate number which depends on the font size).

UILabel *myLabel;
CGSize labelSize = [myLabel.text sizeWithFont:myLabel.font 
                            constrainedToSize:myLabel.frame.size 
                                lineBreakMode:UILineBreakModeWordWrap];
CGFloat labelHeight = labelSize.height;

Hope that helps. If it doesn't work let me know and I'll dig further. Also, untested code, but worked from reference.

For a more complete example, here is code which I put in the viewDidLoad: method of a view controller:

[super viewDidLoad];
UILabel *myLabel = [[UILabel alloc] initWithFrame:CGRectMake(50,50,200,350)];
myLabel.numberOfLines = 0;
myLabel.lineBreakMode = UILineBreakModeWordWrap;
myLabel.text = @"This is some text in a UILabel which is long enough to wrap around the lines in said UILabel. This is a test, this is only a test.";
[self.view addSubview:myLabel];
CGSize labelSize = [myLabel.text sizeWithFont:myLabel.font 
                            constrainedToSize:myLabel.frame.size 
                                lineBreakMode:UILineBreakModeWordWrap];
CGFloat labelHeight = labelSize.height;
NSLog(@"labelHeight = %f", labelHeight);
[myLabel release];

The output from the NSLog goes:

2010-11-15 18:25:27.817 so_labelheight[728:307] labelHeight = 126.000000


For iOS7 and above, the officially sanctioned way to count the number of lines is to use TextKit:

func numberOfLinesForString(string: String, size: CGSize, font: UIFont) -> Int {
    let textStorage = NSTextStorage(string: string, attributes: [NSFontAttributeName: font])

    let textContainer = NSTextContainer(size: size)
    textContainer.lineBreakMode = .ByWordWrapping
    textContainer.maximumNumberOfLines = 0
    textContainer.lineFragmentPadding = 0

    let layoutManager = NSLayoutManager()
    layoutManager.textStorage = textStorage
    layoutManager.addTextContainer(textContainer)

    var numberOfLines = 0
    var index = 0
    var lineRange : NSRange = NSMakeRange(0, 0)
    for (; index < layoutManager.numberOfGlyphs; numberOfLines++) {
        layoutManager.lineFragmentRectForGlyphAtIndex(index, effectiveRange: &lineRange)
        index = NSMaxRange(lineRange)
    }

    return numberOfLines
}


The method -sizeWithFont:constrainedToSize:lineBreakMode is now deprecated. You will want to use the method -boundingRectWithSize:options:attributes:context: now.

Here's an examples:

CGSize boundingRectSize = CGSizeMake(widthToConstrainTo, CGFLOAT_MAX);
NSDictionary *attributes = @{NSFontAttributeName : [UIFont fontWithName:fontName size:14]};
CGRect labelSize = [labelString boundingRectWithSize:boundingRectSize options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading
                                                attributes:attributes
                                                   context:nil];

In the above example I know the width I want to constrain the label to but since I'm not sure of the height, I just max the height param out using CGFLOAT_MAX. For the options you need to use NSStringDrawingUsesLineFragmentOrigin and NSStringDrawingUsesFontLeading if you're trying to calculate the size a label that can be any number of lines.


If you're looking for this answer in Swift 2+/iOS 8, it would look like this:

func numberOfLinesInLabel(yourString: String, labelWidth: CGFloat, labelHeight: CGFloat, font: UIFont) -> Int {

    let paragraphStyle = NSMutableParagraphStyle()
    paragraphStyle.minimumLineHeight = labelHeight
    paragraphStyle.maximumLineHeight = labelHeight
    paragraphStyle.lineBreakMode = .ByWordWrapping

    let attributes: [String: AnyObject] = [NSFontAttributeName: font, NSParagraphStyleAttributeName: paragraphStyle]

    let constrain = CGSizeMake(labelWidth, CGFloat(Float.infinity))

    let size = yourString.sizeWithAttributes(attributes)
    let stringWidth = size.width

    let numberOfLines = ceil(Double(stringWidth/constrain.width))

    return Int(numberOfLines)
}


sizeWithFont is deprecated in iOS 7, you may use this instead:

- (int)lineCountForText:(NSString *) text
{
    UIFont *font = ...

    CGRect rect = [text boundingRectWithSize:CGSizeMake(200, MAXFLOAT)
                                     options:NSStringDrawingUsesLineFragmentOrigin
                                  attributes:@{NSFontAttributeName : font}
                                     context:nil];

    return ceil(rect.size.height / font.lineHeight);
}


(I originally posted my answer here but don't have enough reputation to comment or mark duplicates.)

The other answers I've found don't respect the numberOfLines property of UILabel when it is set to something other than 0.

Here's another option you can add to your category or subclass:

- (NSUInteger)lineCount
{
    CGSize size = [self sizeThatFits:CGSizeMake(self.frame.size.width, CGFLOAT_MAX)];
    return MAX((int)(size.height / self.font.lineHeight), 0);
}

Some notes:

  • I'm using this on a UILabel with attributed text, without ever actually setting the font property, and it's working fine. Obviously you would run into issues if you were using multiple fonts in your attributedText.
  • If you are subclassing UILabel to have custom edge insets (for example by overriding drawTextInRect:, which is a neat trick I found here), then you must remember to take those insets into account when calculating the size above. For example: CGSizeMake(self.frame.size.width - (self.insets.left + self.insets.right), CGFLOAT_MAX)


func getHeight(text:  NSString, width:CGFloat, font: UIFont) -> CGFloat
{
    let rect = text.boundingRect(with: CGSize.init(width: width, height: CGFloat.greatestFiniteMagnitude), options: ([NSStringDrawingOptions.usesLineFragmentOrigin,NSStringDrawingOptions.usesFontLeading]), attributes: [NSFontAttributeName:font], context: nil)
    return rect.size.height
}

text: the Label whose height you need to find with the given string

width: the width of UILabel

font: UILabel's font


Swift 5.5

Based on above good answers and feedbacks, including autolayout.

extension UILabel {
    @objc var numberOfVisibleLines: Int {
        if !translatesAutoresizingMaskIntoConstraints {
            layoutIfNeeded()
        }

        let maxSize = CGSize(width: frame.size.width, height: CGFloat(MAXFLOAT))
        let textHeight = sizeThatFits(maxSize).height
        let lineHeight = font.lineHeight
        let numberOfVisibleLines = lineHeight == 0 ? numberOfLines : Int(ceil(textHeight / lineHeight)) - 1
        return numberOfVisibleLines >= 0 ? numberOfVisibleLines : numberOfLines
    }
}
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号