iOS 文本高度的计算

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

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

你可能感兴趣的:(iOS 文本高度的计算)