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ì)算出來的Height 和 2倍的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)行不同的方法了。
今天就先寫到這里吧
千里之行,始于足下~