UILabel的结果行与UILineBreakModeWordWrap

时间:2010-11-02 20:58:05

标签: iphone cocoa-touch iphone-sdk-3.0 nsstring uilabel

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

一切都很好,标签大小合适,并根据需要显示我的文字。

现在我需要知道用于显示标签的行(或使用sizeWithFont:时生成的行)。我可以在技术上根据空格/插入符号返回来编写我自己的换行实现,但是它不会像Apple的实现那样得到保证,因此生成的行不会是用于计算文本大小的行,没关系重新发明轮子的事实。

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

如何以最优雅的方式实现这一目标?

5 个答案:

答案 0 :(得分:38)

要计算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);

答案 1 :(得分:24)

我认为没有任何灵丹妙药。

这是一个类别方法,似乎适用于我投入的几个基本测试用例。不能保证它不会破坏复杂的东西!

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

我并不认为这是有效率的。更好的方法就是实现自己的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];

答案 2 :(得分:11)

只需调用以下方法,然后传递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 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
}

现在调用此方法: -

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

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

答案 3 :(得分:0)

对于Xcode 7及以上TheTiger的回答需要对以下代码发表评论的更新:

my_array  = [["a","b"],["c","d"],["e","f"]]
p my_hash = my_array.to_h # => {"a"=>"b", "c"=>"d", "e"=>"f"}
p my_hash.key?("c") #  => true

这只会在你使用相同的字体和大小时起作用,它不是一个聪明的举动,但它帮助了我,我想分享它作为我知道的当前解决方案

答案 4 :(得分:0)

针对Swift 3进行了更新

要计算UILabel所包含的行数,在包装文本后,您需要将多行UILabel的高度除以每行的高度(ascender)。

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

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)