【问题标题】:Resulting lines of UILabel with UILineBreakModeWordWrap使用 UILineBreakModeWordWrap 生成的 UILabel 行
【发布时间】:2011-05-04 04:20:17
【问题描述】:

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

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

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

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

任何想法如何以最优雅的方式实现这一点?

【问题讨论】:

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


    【解决方案1】:

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

    【讨论】:

    • 啊,这很聪明。肯定比我的解决方案简单!
    • 请注意,"leading" 读作“ledding”。 (就像在“Led Zepplin”中一样,而不是在“跟随领导者”中。)它只是指金属,被引导(即原子物质 Pb)——这个词来自印刷术的早期,当你字面上放置 LED 条时在字母之间创建,嗯,领导。
    • 谢谢 .... [label sizeToFit]; int numLines = (int)(label.frame.size.height/label.font.leading);为我工作
    【解决方案2】:

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

    这是一个类别方法,似乎适用于我提供的几个基本测试用例。不能保证它不会因复杂的事情而崩溃!

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

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

    【讨论】:

    • 感谢 TomSwift 的输入...这种方法在某些情况下有效,但它确实给了我很大的推动力,我正在根据您的代码开发一个更加防弹的解决方案。再次感谢!
    • 不错的解决方案 TomSwift。但这对我不起作用,它始终是一行上的文本,尽管 int 计数有时是 9、10 或 20。我的代码:self.labelDescription.text = event.description; self.labelDescription.lineBreakMode = UILineBreakModeWordWrap; self.labelDescription.font = [UIFont fontWithName:@"Helvetica" size:13.0]; NSArray* 行 = [self.labelDescription 行]; int count = [行数]; self.labelDescription.numberOfLines = 计数;
    • 不错的解决方案,但对我来说有一个问题,最后两行总是相同的。我通过添加 currentLine = [self.text substringWithRange: rRemainingText];在继续之前;
    【解决方案3】:

    只需调用下面的方法并传递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
    }
    

    现在调用这个方法:-

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

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

    【讨论】:

    • 这很好用,但是当我将 xcode 更新为 7 时,它在 lineCount = rHeight/charSize; 行显示“exc_arithmetic code=exc_i386_div”错误.所以我做了 lineCount = rHeight/1;现在它正在工作。如果我错了指导我纠正一个:)
    【解决方案4】:

    对于 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;
    }
    

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

    【讨论】:

      【解决方案5】:

      为 Swift 3 更新

      要计算 UILabel 的行数,在换行后,您需要将多行 UILabel 的高度除以每行的高度 (ascender)。

      在尝试 Adolfo 的答案时,由于某种原因,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)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-06-03
        • 1970-01-01
        • 2018-08-30
        相关资源
        最近更新 更多