UILabel 与 UILineBreakModeWordWrap 的结果行

发布于 2024-09-30 00:46:36 字数 414 浏览 8 评论 0原文

我有一个 UILabel,其大小是使用 sizeWithFont: 方法计算的。换行模式设置为 UILineBreakModeWordWrap (使用 sizeWithFont: 计算大小时使用相同的标志)...

一切都很好,标签大小正确并显示我的文本根据需要。

现在我需要知道用于显示标签的行(或使用 sizeWithFont: 时生成的行)。从技术上讲,我可以根据空格/插入符返回编写自己的换行实现,但是不能保证它与 Apple 的实现方式相同,因此生成的行将不是用于计算文本大小的行,不要介意重新发明轮子的事实。

理想情况下,我会传递字符串,指定宽度和换行模式,并接收表示文本可视行的字符串数组。

有什么想法如何以最优雅的方式实现这一点吗?

I have a UILabel whose size is calculated with sizeWithFont: method. The line break mode is set to UILineBreakModeWordWrap (same flag is used when calculating the size with sizeWithFont:)...

Everything works great, label is properly sized and displays my text as required.

Now I need to know the lines that are used to display the label (or the lines that are generated when sizeWithFont: is used). I could technically write my own implementation of line breaking based on spaces/caret returns, but then it's not going to be guaranteed the same way as Apple's implementation and hence the resulting lines will not be the ones that are used to calculate the size of text, nevermind the fact of reinventing the wheel.

Ideally, I would pass my string, specify the width and line break mode and receive an array of strings representing the visual lines of text.

Any ideas how to make this happen in the most elegant way?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

甜妞爱困 2024-10-07 00:46:36

要计算 UILabel 在换行文本后的行数,您需要找到 UILabel 字体的前导(行高)(label .font.leading),然后将多行 UILabel 的高度除以每行的高度即可得出行数。

这是一个例子:

- (void)viewDidLoad {

    [super viewDidLoad];

    UILabel *label = [[[UILabel alloc] initWithFrame:CGRectZero] autorelease];
    label.numberOfLines = 0;
    label.lineBreakMode = UILineBreakModeWordWrap;  
    label.text = @"Some really really long string that will cause the label's text to wrap and wrap and wrap around. Some really really long string that will cause the label's text to wrap and wrap and wrap around.";

    CGRect frame = label.frame;
    frame.size.width = 150.0f;
    frame.size = [label sizeThatFits:frame.size];
    label.frame = frame;

    CGFloat lineHeight = label.font.leading;
    NSUInteger linesInLabel = floor(frame.size.height/lineHeight);
    NSLog(@"Number of lines in label: %i", linesInLabel);

    [self.view addSubview:label];

}

或者,你可以用两行来完成:

[label sizeToFit];
int numLines = (int)(label.frame.size.height/label.font.leading);

To calculate the number of lines that a UILabel has after wrapping it's text you will need to find the leading (line height) of your UILabel's font (label.font.leading) and then divide the height of your multi-line UILabel by the height of each line to yield the number of lines.

Here's an example:

- (void)viewDidLoad {

    [super viewDidLoad];

    UILabel *label = [[[UILabel alloc] initWithFrame:CGRectZero] autorelease];
    label.numberOfLines = 0;
    label.lineBreakMode = UILineBreakModeWordWrap;  
    label.text = @"Some really really long string that will cause the label's text to wrap and wrap and wrap around. Some really really long string that will cause the label's text to wrap and wrap and wrap around.";

    CGRect frame = label.frame;
    frame.size.width = 150.0f;
    frame.size = [label sizeThatFits:frame.size];
    label.frame = frame;

    CGFloat lineHeight = label.font.leading;
    NSUInteger linesInLabel = floor(frame.size.height/lineHeight);
    NSLog(@"Number of lines in label: %i", linesInLabel);

    [self.view addSubview:label];

}

Or, you could do it in two lines:

[label sizeToFit];
int numLines = (int)(label.frame.size.height/label.font.leading);
落在眉间の轻吻 2024-10-07 00:46:36

我认为这没有什么灵丹妙药。

这是一个类别方法,似乎适用于我扔给它的几个基本测试用例。不保证它不会因为复杂的东西而崩溃!

它的工作方式是通过字符串测试来查看一系列单词是否适合标签的宽度。当它计算出当前范围太宽时,它会将最后拟合的范围记录为一条线。

我并不认为这是有效的。更好的方法可能就是实现你自己的 UILabel...

@interface UILabel (Extensions)

- (NSArray*) lines;

@end

@implementation UILabel (Extensions)

- (NSArray*) lines
{
    if ( self.lineBreakMode != UILineBreakModeWordWrap )
    {
        return nil;
    }

    NSMutableArray* lines = [NSMutableArray arrayWithCapacity:10];

    NSCharacterSet* wordSeparators = [NSCharacterSet whitespaceAndNewlineCharacterSet];

    NSString* currentLine = self.text;
    int textLength = [self.text length];

    NSRange rCurrentLine = NSMakeRange(0, textLength);
    NSRange rWhitespace = NSMakeRange(0,0);
    NSRange rRemainingText = NSMakeRange(0, textLength);
    BOOL done = NO;
    while ( !done )
    {
        // determine the next whitespace word separator position
        rWhitespace.location = rWhitespace.location + rWhitespace.length;
        rWhitespace.length = textLength - rWhitespace.location;
        rWhitespace = [self.text rangeOfCharacterFromSet: wordSeparators options: NSCaseInsensitiveSearch range: rWhitespace];
        if ( rWhitespace.location == NSNotFound )
        {
            rWhitespace.location = textLength;
            done = YES;
        }

        NSRange rTest = NSMakeRange(rRemainingText.location, rWhitespace.location-rRemainingText.location);

        NSString* textTest = [self.text substringWithRange: rTest];

        CGSize sizeTest = [textTest sizeWithFont: self.font forWidth: 1024.0 lineBreakMode: UILineBreakModeWordWrap];
        if ( sizeTest.width > self.bounds.size.width )
        {
            [lines addObject: [currentLine stringByTrimmingCharactersInSet:wordSeparators]];
            rRemainingText.location = rCurrentLine.location + rCurrentLine.length;
            rRemainingText.length = textLength-rRemainingText.location;
            continue;
        }

        rCurrentLine = rTest;
        currentLine = textTest;
    }

    [lines addObject: [currentLine stringByTrimmingCharactersInSet:wordSeparators]];

    return lines;
}

@end

使用如下:

NSArray* lines = [_theLabel lines];

int count = [lines count];

I don't think there is any silver bullet for this.

Here is a category method that seems to work for the few basic test cases I threw at it. No guarantees it won't break with something complex!

The way it works is to move through the string testing to see if a range of words fits in the width of the label. When it calculates that the current range is too wide it records the last-fitting range as a line.

I don't claim this is efficient. A better way may just to be to implement your own UILabel...

@interface UILabel (Extensions)

- (NSArray*) lines;

@end

@implementation UILabel (Extensions)

- (NSArray*) lines
{
    if ( self.lineBreakMode != UILineBreakModeWordWrap )
    {
        return nil;
    }

    NSMutableArray* lines = [NSMutableArray arrayWithCapacity:10];

    NSCharacterSet* wordSeparators = [NSCharacterSet whitespaceAndNewlineCharacterSet];

    NSString* currentLine = self.text;
    int textLength = [self.text length];

    NSRange rCurrentLine = NSMakeRange(0, textLength);
    NSRange rWhitespace = NSMakeRange(0,0);
    NSRange rRemainingText = NSMakeRange(0, textLength);
    BOOL done = NO;
    while ( !done )
    {
        // determine the next whitespace word separator position
        rWhitespace.location = rWhitespace.location + rWhitespace.length;
        rWhitespace.length = textLength - rWhitespace.location;
        rWhitespace = [self.text rangeOfCharacterFromSet: wordSeparators options: NSCaseInsensitiveSearch range: rWhitespace];
        if ( rWhitespace.location == NSNotFound )
        {
            rWhitespace.location = textLength;
            done = YES;
        }

        NSRange rTest = NSMakeRange(rRemainingText.location, rWhitespace.location-rRemainingText.location);

        NSString* textTest = [self.text substringWithRange: rTest];

        CGSize sizeTest = [textTest sizeWithFont: self.font forWidth: 1024.0 lineBreakMode: UILineBreakModeWordWrap];
        if ( sizeTest.width > self.bounds.size.width )
        {
            [lines addObject: [currentLine stringByTrimmingCharactersInSet:wordSeparators]];
            rRemainingText.location = rCurrentLine.location + rCurrentLine.length;
            rRemainingText.length = textLength-rRemainingText.location;
            continue;
        }

        rCurrentLine = rTest;
        currentLine = textTest;
    }

    [lines addObject: [currentLine stringByTrimmingCharactersInSet:wordSeparators]];

    return lines;
}

@end

use like this:

NSArray* lines = [_theLabel lines];

int count = [lines count];
傾城如夢未必闌珊 2024-10-07 00:46:36

只需调用下面的方法并传递 UILabelUITextView

-(NSInteger)getNumberOfLinesInLabelOrTextView:(id)obj
{
    NSInteger lineCount = 0;
    if([obj isKindOfClass:[UILabel class]])
    {
        UILabel *label = (UILabel *)obj;

       // This method is deprecated in iOS 7.0 or later 
       // CGSize requiredSize = [label.text sizeWithFont:label.font constrainedToSize:label.frame.size lineBreakMode:label.lineBreakMode]; 

        CGSize requiredSize = [label.text boundingRectWithSize:CGSizeMake(CGRectGetWidth(label.frame), CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:label.font} context:nil].size;

        int charSize = label.font.leading;
        int rHeight = requiredSize.height;

        lineCount = rHeight/charSize;
    }
    else if ([obj isKindOfClass:[UITextView class]])
    {
        UITextView *textView = (UITextView *)obj;
        lineCount = textView.contentSize.height / textView.font.leading;
    }

    return lineCount;
}

现在调用此方法:-

NSLog(@"%d",[self getNumberOfLinesInLabelOrTextView:label]);
NSLog(@"%d",[self getNumberOfLinesInLabelOrTextView:textView]);

更新:SWIFT 代码

func getNumberOfLinesInLabelOrTextView(obj:AnyObject) -> NSInteger {

    var lineCount: NSInteger = 0
    if (obj.isKindOfClass(UILabel)) {

        let label: UILabel = obj as! UILabel
        let requiredSize: CGSize = (label.text)!.boundingRectWithSize(CGSizeMake(CGRectGetWidth(label.frame), CGFloat.max), options: NSStringDrawingOptions.UsesLineFragmentOrigin, attributes: [NSFontAttributeName: label.font], context: nil).size
        let charSize: CGFloat = label.font.leading
        let rHeight: CGFloat = requiredSize.height
        lineCount = (NSInteger)(rHeight/charSize)
    }
    else if (obj.isKindOfClass(UITextView)){

        let textView: UITextView = obj as! UITextView
        lineCount = (NSInteger)(textView.contentSize.height / textView.font.leading)
    }

    return lineCount
}

< strong>现在调用此方法:-

println("%d \(self.getNumberOfLinesInLabelOrTextView(textView))")
println("%d \(self.getNumberOfLinesInLabelOrTextView(label))")

注意:leading - 使用 lineHeight。不返回实际领先。将来将被正式弃用。

Just call below method and pass either UILabel or UITextView:

-(NSInteger)getNumberOfLinesInLabelOrTextView:(id)obj
{
    NSInteger lineCount = 0;
    if([obj isKindOfClass:[UILabel class]])
    {
        UILabel *label = (UILabel *)obj;

       // This method is deprecated in iOS 7.0 or later 
       // CGSize requiredSize = [label.text sizeWithFont:label.font constrainedToSize:label.frame.size lineBreakMode:label.lineBreakMode]; 

        CGSize requiredSize = [label.text boundingRectWithSize:CGSizeMake(CGRectGetWidth(label.frame), CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:label.font} context:nil].size;

        int charSize = label.font.leading;
        int rHeight = requiredSize.height;

        lineCount = rHeight/charSize;
    }
    else if ([obj isKindOfClass:[UITextView class]])
    {
        UITextView *textView = (UITextView *)obj;
        lineCount = textView.contentSize.height / textView.font.leading;
    }

    return lineCount;
}

Now call this method:-

NSLog(@"%d",[self getNumberOfLinesInLabelOrTextView:label]);
NSLog(@"%d",[self getNumberOfLinesInLabelOrTextView:textView]);

UPDATED: SWIFT CODE

func getNumberOfLinesInLabelOrTextView(obj:AnyObject) -> NSInteger {

    var lineCount: NSInteger = 0
    if (obj.isKindOfClass(UILabel)) {

        let label: UILabel = obj as! UILabel
        let requiredSize: CGSize = (label.text)!.boundingRectWithSize(CGSizeMake(CGRectGetWidth(label.frame), CGFloat.max), options: NSStringDrawingOptions.UsesLineFragmentOrigin, attributes: [NSFontAttributeName: label.font], context: nil).size
        let charSize: CGFloat = label.font.leading
        let rHeight: CGFloat = requiredSize.height
        lineCount = (NSInteger)(rHeight/charSize)
    }
    else if (obj.isKindOfClass(UITextView)){

        let textView: UITextView = obj as! UITextView
        lineCount = (NSInteger)(textView.contentSize.height / textView.font.leading)
    }

    return lineCount
}

Now call this method:-

println("%d \(self.getNumberOfLinesInLabelOrTextView(textView))")
println("%d \(self.getNumberOfLinesInLabelOrTextView(label))")

Note: leading - use lineHeight. does not return actual leading. will be formally deprecated in future.

攒一口袋星星 2024-10-07 00:46:36

对于 Xcode 7 及更高版本,TheTiger 的答案需要对以下代码进行更新注释:

-(NSInteger)getNumberOfLinesInLabelOrTextView:(id)obj
{
    NSInteger lineCount = 0;
    if([obj isKindOfClass:[UILabel class]])
    {
        UILabel *label = (UILabel *)obj;
        CGSize requiredSize = [label.text boundingRectWithSize:CGSizeMake(CGRectGetWidth(label.frame), CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:label.font} context:nil].size;

        int charSize = label.font.leading;
        // now listen , you need to set the text or label with only 1 
        // then nslog(@"%d",charSize);
        // then change the line int charSize = label.font.leading; into 
        // int charSize = the printed value in case of 1 line
        int rHeight = requiredSize.height;

        lineCount = rHeight/charSize;
    }
    else if ([obj isKindOfClass:[UITextView class]])
    {
        UITextView *textView = (UITextView *)obj;
        lineCount = textView.contentSize.height / textView.font.leading;
    }

    return lineCount;
}

这仅在您使用相同的字体和大小时才有效,这不是一个明智的举动,但它帮助了我,我想将其作为我知道的当前解决方案进行分享

for Xcode 7 and up TheTiger's answer needs an update commented on the code below :

-(NSInteger)getNumberOfLinesInLabelOrTextView:(id)obj
{
    NSInteger lineCount = 0;
    if([obj isKindOfClass:[UILabel class]])
    {
        UILabel *label = (UILabel *)obj;
        CGSize requiredSize = [label.text boundingRectWithSize:CGSizeMake(CGRectGetWidth(label.frame), CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:label.font} context:nil].size;

        int charSize = label.font.leading;
        // now listen , you need to set the text or label with only 1 
        // then nslog(@"%d",charSize);
        // then change the line int charSize = label.font.leading; into 
        // int charSize = the printed value in case of 1 line
        int rHeight = requiredSize.height;

        lineCount = rHeight/charSize;
    }
    else if ([obj isKindOfClass:[UITextView class]])
    {
        UITextView *textView = (UITextView *)obj;
        lineCount = textView.contentSize.height / textView.font.leading;
    }

    return lineCount;
}

this will only work if you are using same font and size , it's not a smart move but it helped me and i wanted to share it as the current solution i know

黑寡妇 2024-10-07 00:46:36

更新为 Swift 3

要计算 UILabel 的行数,在换行文本后,您需要将多行 UILabel 的高度除以每行的高度 (ascender< /代码>)。

当尝试阿道夫的答案时,由于某种原因,label.font.leading返回0.0,所以我使用label.font.ascender,它返回从基线到顶部的高度UILabel 的框架。见下图。

输入图片此处描述

//Makes label go to another line if necessary
label.numberOfLines = 0 //Set num of lines to infinity
label.lineBreakMode = .byWordWrapping
label.sizeToFit()
let numLines = Int(label.frame.size.height/label.font.ascender)

Updated for Swift 3

To calculate the number of lines that UILabel has, after wrapping its text, you need to divide the height of your multi-line UILabel by the height of each line (ascender).

When trying Adolfo's answer, for some reason, label.font.leading returned 0.0, so I used label.font.ascender, which returns the height from the baseline to the top of the UILabel's frame. See picture below.

enter image description here

//Makes label go to another line if necessary
label.numberOfLines = 0 //Set num of lines to infinity
label.lineBreakMode = .byWordWrapping
label.sizeToFit()
let numLines = Int(label.frame.size.height/label.font.ascender)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文