iOS 文本高度的计算

作者: 夜满西楼 | 来源:发表于2016-04-11 16:13 被阅读9991次

    iOS开发的过程中,总是避免不了各种高度的自适应,如:UILabel、UITextView、UITableViewCell的高度自适应...而这些适应大部分都源自文本的适应。
    计算文本高度的方法有很多种,而我们平时的使用中,富文本的使用几率要大于普通文本,下面以富文本为例介绍两种获取文本高度的方式:

    第一种:通过UILabel获取文本的高度

        NSString *str = @"朱雀桥边野草花,乌衣巷口夕阳斜。旧时王谢堂前燕,飞入寻常百姓家。";
        NSMutableAttributedString *attributeString = [[NSMutableAttributedString alloc] initWithString:str];
        NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init];
        style.lineSpacing = 10;
        UIFont *font = [UIFont systemFontOfSize:14];
        [attributeString addAttribute:NSParagraphStyleAttributeName value:style range:NSMakeRange(0, str.length)];
        [attributeString addAttribute:NSFontAttributeName value:font range:NSMakeRange(0, str.length)];
        UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 200, 1)];
        label.font = [UIFont systemFontOfSize:14];
        label.numberOfLines = 0;
        label.attributedText = attributeString;
        CGSize size = [label sizeThatFits:CGSizeMake(label.frame.size.width, CGFLOAT_MAX)];
        NSLog(@"size:%@", NSStringFromCGSize(size));
        NSLog(@"label.frame.size:%@", NSStringFromCGSize(label.frame.size));
    

    可以看到打印结果为:

    size:{196, 70.5}
    label.frame.size:{200, 1}
    

    在label调用完sizeThatFits:之后,会返回根据条件适应好的size(本身的size并不发生改变),可以通过size.height拿到文本适应后的高度。
    但是这种方法有些时候取到的高度会有问题,比如要显示的文本为:@"朱雀桥边野草花"时,上面的代码打印的结果为:

    size:{98, 27}  //单行高度包含了行间距
    label.frame.size:{200, 1}
    

    由于上面的代码显示的文本是富文本,文本的高度包含文字高度的同时,还包含有行间距,但是当UILabel显示的内容为1行时,label调用完sizeThatFits:之后返回的size高度是包含行间距的。

    当UILabel显示的文本行数大于等于2行时,sizeThatFits:后拿到的高度是准确的,当UILabel显示的文本行数是1行时,拿到的高度需要减去行间距后才是正确的适应高度

    第二种:调用字符串的boundingRectWithSize获取文本的高度

        NSString *str = @"朱雀桥边野草花,乌衣巷口夕阳斜。旧时王谢堂前燕,飞入寻常百姓家。";
        NSMutableAttributedString *attributeString = [[NSMutableAttributedString alloc] initWithString:str];
        NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init];
        style.lineSpacing = 10;
        UIFont *font = [UIFont systemFontOfSize:14];
        [attributeString addAttribute:NSParagraphStyleAttributeName value:style range:NSMakeRange(0, str.length)];
        [attributeString addAttribute:NSFontAttributeName value:font range:NSMakeRange(0, str.length)];
        NSStringDrawingOptions options = NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading;
        CGRect rect = [attributeString boundingRectWithSize:CGSizeMake(200, CGFLOAT_MAX) options:options context:nil];
        NSLog(@"size:%@", NSStringFromCGSize(rect.size));
    

    可以看到打印结果为:

    size:{196, 70.12109375}
    

    要显示的文本为:@"朱雀桥边野草花"时,上面的代码打印的结果为:

    size:{98, 26.70703125}  //单行文本的行高同样包含行间距
    

    以上两种方式都能获取到文本的高度,同样在显示富文本时,如果文本的行数是1行,获取到的文本高度都有问题,我们期望得到的是文本的高度(不包含行间距),这时候就需要判断了。

    //文本的高度减去字体高度小于等于行间距,判断为当前只有1行
    if ((rect.size.height - font.lineHeight) <= style.lineSpacing) {
            rect = CGRectMake(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height-style.lineSpacing);
        }
    

    到这里当文本只有1行时,获取准确的高度的问题好像得到了解决。当现实的文本是@"朱雀桥边野草花"时,打印的结果为:

    size:{98, 26.70703125}  //加判断条件之前
    size:{98, 16.70703125}  //加判断条件之后
    

    获取的高度确实为字体的高度,不包含行间距了,然而在测试的时候悲催的发现,当显示文本是@"sfklsdkfjsdlfjsdjfd"时,纯英文的一段测试数据,上面代码的打印结果为:

    size:{113.4013671875, 6.70703125}
    

    唉,我去,高度怎么变成这么点儿了?在反复测试之后发现,只要文本中包含有中文时,计算单行文本高度就包含行间距,在文本是纯英文时,单行文本的高度不包含行间距(注:2行或2行以上的纯英文计算的高度是没问题的),so...判断条件又变成了下边这样:

    if ((rect.size.height - _font.lineHeight) <= paragraphStyle.lineSpacing) {
            if ([self containChinese:str]) {  //如果包含中文
                rect = CGRectMake(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height-paragraphStyle.lineSpacing);
            }
        }
    
    //判断如果包含中文
    - (BOOL)containChinese:(NSString *)str {
        for(int i=0; i< [str length];i++){
            int a = [str characterAtIndex:i];
            if( a > 0x4e00 && a < 0x9fff){
                return YES;
            }
        }
        return NO;
    }
    

    到这里问题就算是解决了,由于笔者能力有限,只会利用现掌握的知识解决这种问题,如果有大神有好的办法,还望不吝赐教,分享一下!

    相关文章

      网友评论

      • 席列2:TTTAttributedLabel可以解决这个问题
      • Cass__:Den 你好,你的文章解决了我的困扰。折腾了好久,终于解决了。
        我在你文章的基础在又整理了一下。
        http://www.jianshu.com/p/a7f55e456539
        感谢您。
        Cass__:@Den_co 嘿嘿 希望你看到你持续的更新。难道你有另外的博客吗?
        夜满西楼:@CassCai 很高兴能够帮到你,哈哈,大家一起进步。
      • 惊诧的海胆:我也是被这个问题弄得头疼..... 还有中文的换行模式 wordwrap 对中文是无效的.

      本文标题:iOS 文本高度的计算

      本文链接:https://www.haomeiwen.com/subject/wvrilttx.html