iOS開發(fā)之計(jì)算文本高度

iOS開發(fā)的過程中,總是避免不了各種高度的自適應(yīng),如:UILabel、UITextView、UITableViewCell的高度自適應(yīng)...而這些適應(yīng)大部分都源自文本的適應(yīng)。
計(jì)算文本高度的方法有很多種,而我們平時(shí)的使用中,富文本的使用幾率要大于普通文本,下面以富文本為例介紹三種獲取文本高度的方式:

方法1.0:

請看運(yùn)行效果:

方法一的效果

1.創(chuàng)建label

UILabel *contentLab = [[UILabel alloc]initWithFrame:CGRectMake(20, 64,300, 20)];
contentLab.font = [UIFont systemFontOfSize:15];
contentLab.backgroundColor = [UIColor greenColor];
contentLab.numberOfLines = 0;
contentLab.text = @"如果包含漢字的文本一行就可以顯示完,就不需要設(shè)置富文本,否則會(huì)出現(xiàn)文本錯(cuò)位的問題.";
[self.view addSubview:contentLab];

2.獲取高度 重置坐標(biāo)

CGFloat Height = [self getTextHeightWithStr:contentLab.text
                                  withWidth:300
                            withLineSpacing:10//(預(yù)設(shè)的行間距)
                                   withFont:15];
    
contentLab.frame = CGRectMake(20,64, 300, Height);

3.如果包含漢字的文本一行就可以顯示完,就不需要設(shè)置富文本,否則會(huì)出現(xiàn)文本錯(cuò)位的問題.
但是什么時(shí)候一行顯示 還需要自己判斷,而且估算的不是十分準(zhǔn)確,比較麻煩

    /*我在此處的處理方法是把 計(jì)算出來的Height 和 2倍的font高度做比較,
      如果是多行顯示,文本高度加上行間距肯定大于2倍的font,
      滿足條件,就是 多行,設(shè)置行間距.
     */
if (Height > 2*contentLab.font.lineHeight){//多行顯示

    NSMutableParagraphStyle *paraStyle  = [[NSMutableParagraphStyle alloc] init];
    paraStyle.lineSpacing = 10;
    NSMutableAttributedString *attributeString = [[NSMutableAttributedString alloc]initWithString:contentLab.text
                                                                                       attributes:@{NSParagraphStyleAttributeName:paraStyle,NSFontAttributeName:[UIFont systemFontOfSize:15]}];
    contentLab.attributedText = attributeString;
}
 
    /**
     出現(xiàn)問題的條件:
     條件一:當(dāng)前文本內(nèi)容'一行'就可以顯示完全
     條件二:文本當(dāng)中包含'漢字'
     結(jié)果:計(jì)算出來的高度即使只有一行,也會(huì)包'含行間距'
     
     解決辦法:
     在封裝方法中:
     條件一: '文本高度' - '字體高度',如果只有一行,'差'肯定 <= 行間距
     條件二: 文本中有漢字
     */
    

實(shí)現(xiàn)原理:

/**
 計(jì)算文本高度
 
 @param str         文本內(nèi)容
 @param width       lab寬度
 @param lineSpacing 行間距(沒有行間距就傳0)
 @param font        文本字體大小
 
 @return 文本高度
 */
-(CGFloat)getTextHeightWithStr:(NSString *)str
                     withWidth:(CGFloat)width
               withLineSpacing:(CGFloat)lineSpacing
                      withFont:(CGFloat)font
{
    if (!str || str.length == 0) {
        return 0;
    }
    NSMutableParagraphStyle *paraStyle = [[NSMutableParagraphStyle alloc]init];
    paraStyle.lineSpacing = lineSpacing;
    NSMutableAttributedString *attributeString = [[NSMutableAttributedString alloc]initWithString:str
                                                                                       attributes:@{NSParagraphStyleAttributeName:paraStyle,NSFontAttributeName:[UIFont systemFontOfSize:font]}];
    
    CGRect rect = [attributeString boundingRectWithSize:CGSizeMake(width, MAXFLOAT)
                                                options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading
                                                context:nil];

    if ((rect.size.height - [UIFont systemFontOfSize:font].lineHeight)  <= lineSpacing){
        if ([self containChinese:str]){
            rect.size.height -= lineSpacing;
        }
    }
    return rect.size.height;
}
//判斷是否包含中文
- (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;
}
總結(jié):

方法1.0在使用的時(shí)候要用計(jì)算出來的Height2倍的font高度比較,如果是多行顯示,文本高度加上行間距肯定大于2倍的font,滿足條件,就是 多行,設(shè)置行間距.
否則不能使用行間距,會(huì)出現(xiàn)錯(cuò)誤的問題(隨著行間距大于5之后會(huì)越來越明顯).

方法 1.1:(和方法1.0相比,判斷是否需要多行顯示的方法不一樣)

1.創(chuàng)建Label(和上面創(chuàng)建的一樣)

2.(封裝的方法) 判斷當(dāng)前對象的內(nèi)容 是否 需要多行顯示

CGFloat lineSpacing = 0;
if ([self isNeedMultiLineWithStr:contentLab.text withHeight:100 withWidth:300 withFont:15]) {

    NSMutableParagraphStyle *paraStyle = [[NSMutableParagraphStyle alloc] init];

    //只有多行顯示才設(shè)置行間距 否則會(huì)錯(cuò)位     
    paraStyle.lineSpacing = 5;
    NSMutableAttributedString *attributeString = [[NSMutableAttributedString alloc]initWithString:contentLab.text
                                                                                       attributes:@{NSParagraphStyleAttributeName:paraStyle,NSFontAttributeName:[UIFont systemFontOfSize:15]}];
    contentLab.attributedText = attributeString;
        
    lineSpacing = paraStyle.lineSpacing;
}

3.獲取高度 重置坐標(biāo)

CGFloat Height = [self getTextHeightWithStr:contentLab.text
                                  withWidth:300
                            withLineSpacing:lineSpacing
                                   withFont:15];

contentLab.frame = CGRectMake(0,64, 300, Height);

實(shí)現(xiàn)原理:

/**
 判斷是否超過一行

 @param str    文本內(nèi)容
 @param height 文本高度
 @param width  文本寬度
 @param font   文本描述

 @return 返回判斷結(jié)果
 */
-(BOOL)isNeedMultiLineWithStr:(NSString *)str
                   withHeight:(CGFloat)height
                    withWidth:(CGFloat)width
                     withFont:(CGFloat)font
{
    
    CGFloat myWidth = [str boundingRectWithSize:CGSizeMake(MAXFLOAT,height)
                                               options:NSStringDrawingUsesFontLeading|NSStringDrawingUsesLineFragmentOrigin
                                            attributes:@{NSFontAttributeName :[UIFont systemFontOfSize:font]}
                                               context:nil].size.width;
    
    
    if (myWidth <= width) {
        return NO;
    }
    else{
        return YES;
    }
}
總結(jié):

和上面的方法相比較,其實(shí)就是判斷 需不需要多行顯示文本 的方法不一樣,計(jì)算文本高度的核心算法是一樣的.

方法二:(簡單好用)

1.創(chuàng)建Label(同上)

2. (封裝的方法)判斷當(dāng)前對象的內(nèi)容 是否 需要多行顯

//當(dāng)文本中有'漢字' 且 '一行' 就可以顯示完的時(shí)候,此時(shí)設(shè)置行間距就會(huì)導(dǎo)致計(jì)算不準(zhǔn)確,出現(xiàn)錯(cuò)位情況,必須加上判斷條件.
if ([self isNeedMultiLineWithStr:contentLab.text
                          Height:100
                           Width:300
                            Font:15]) {

    NSMutableParagraphStyle *paraStyle = [[NSMutableParagraphStyle alloc] init];
    
    //只有多行顯示才設(shè)置行間距 否則會(huì)錯(cuò)位
    paraStyle.lineSpacing = 5;
    NSMutableAttributedString *attributeString = [[NSMutableAttributedString alloc]initWithString:contentLab.text
                                                                                       attributes:@{NSParagraphStyleAttributeName:paraStyle,NSFontAttributeName:[UIFont systemFontOfSize:15]}];
    contentLab.attributedText = attributeString;
    
}

3.計(jì)算Size (只此一步,短小精悍)

CGSize size = [contentLab sizeThatFits:CGSizeMake(contentLab.frame.size.width, CGFLOAT_MAX)];

contentLab.frame = CGRectMake(0,64, 300, size.height);

實(shí)現(xiàn)原理:(同方法1.1的判斷方法一樣)

總結(jié):

該方法計(jì)算文本高度省去不少代碼,計(jì)算也同樣準(zhǔn)確,值得推薦.

方法三:(C函數(shù))

首先導(dǎo)入系統(tǒng)的核心文本庫#import <CoreText/CoreText.h>

1.創(chuàng)建Label(同上)

2. (封裝的方法)判斷當(dāng)前對象的內(nèi)容 是否 需要多行顯(同方法二的第二步一樣)

3.計(jì)算文本高度

CGFloat Height = [self getTextHeightWithStr:contentLab.text
                                  withWidth:300
                            withLineSpacing:lineSpacing
                                   withFont:15];

contentLab.frame = CGRectMake(0,64, 300, Height);

實(shí)現(xiàn)原理:

/**
 計(jì)算文本高度
 
 @param str         文本內(nèi)容
 @param width       lab寬度
 @param lineSpacing 行間距(沒有行間距就傳0)
 @param font        文本字體大小
 
 @return 文本高度
 */
-(CGFloat)getTextHeightWithStr:(NSString *)str
                     withWidth:(CGFloat)width
               withLineSpacing:(CGFloat)lineSpacing
                      withFont:(CGFloat)font
{
    if (!str || str.length == 0) {
        return 0;
    }
    NSMutableParagraphStyle *paraStyle = [[NSMutableParagraphStyle alloc] init];
    paraStyle.lineSpacing =  lineSpacing;
    NSMutableAttributedString *attStr = [[NSMutableAttributedString alloc] initWithString:str attributes:@{NSParagraphStyleAttributeName:paraStyle,NSFontAttributeName:[UIFont systemFontOfSize:font]}];
    
    CTFramesetterRef frameSetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attStr);
    CGSize attSize = CTFramesetterSuggestFrameSizeWithConstraints(frameSetter, CFRangeMake(0, 0), NULL,CGSizeMake(width, CGFLOAT_MAX), NULL);
    CFRelease(frameSetter);
    
    return attSize.height;

}

總結(jié):

在上面這么多的方法中,一共有2種判斷是否需要多行顯示的方法,3種計(jì)算文本高度的算法.如果把這些組合起來將有6種計(jì)算文本高度的方法,大家自由發(fā)揮.其實(shí)原理都是一樣的.所需要解決的問題也是一樣的(就是一行能顯示全的情況下,而且文本中包含有漢字,不能設(shè)置行間距,會(huì)出現(xiàn)錯(cuò)位的情況).
最好在使用的時(shí)候封裝在工具類當(dāng)中 或者 category中.這樣在計(jì)算的時(shí)候就不需要每個(gè)類中重復(fù)實(shí)現(xiàn)該方法.

我把Demo放在gitHub上面了,有想看的可以download看看~


我在4個(gè)viewController中實(shí)現(xiàn)了不同的方法,切換window的跟視圖就可以運(yùn)行不同的方法了。

今天就先寫到這里吧
千里之行,始于足下~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容