最近項目有這樣一個需求氮发,UITableViewCell中有一段文本渴肉,最多顯示6行,超過6行就折疊為4行爽冕,還可點擊全文
展開全文仇祭。為了避免UITaleView滑動的時候cell高度不確定帶來的頁面卡頓問題,就尋思能否提前獲取文本的高度颈畸,因為有文本折疊的問題乌奇,需要知道文本一共有多少行,折疊為4行高度是多少眯娱,展開全文高度又是多少礁苗,順著這個思路就發(fā)現(xiàn)了CoreText,然后再結合YYText中YYLabel的源碼,總結一下
先附一張實現(xiàn)代碼:
//string轉AttributedString
NSMutableAttributedString *attStr = [[NSMutableAttributedString alloc] initWithString:self.text];
//
CFMutableAttributedStringRef attrString = (__bridge CFMutableAttributedStringRef)attStr;
//設置字體
CTFontRef font = CTFontCreateUIFontForLanguage(kCTFontUserFontType, [Font15 pointSize], NULL);
CFAttributedStringSetAttribute(attrString, CFRangeMake(0, CFAttributedStringGetLength(attrString)), kCTFontAttributeName, font);
//創(chuàng)建 CTFramesetterRef
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(attrString);
// 文本繪制區(qū)域徙缴,寬度必須明確试伙,高度給了一個超大值,不然會出現(xiàn)繪制不全的情況
CGPathRef path = CGPathCreateWithRect(CGRectMake(0,0,kbScreenWidth-30,10000), NULL);
CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), path, NULL);
// 獲取所有行數(shù)組
CFArrayRef lines = CTFrameGetLines(frame);
// 有多少行
size_t numOfLines = CFArrayGetCount(lines);
// 計算出每一行的origins坐標于样,存放在一個數(shù)組
CGPoint lineOrigins[numOfLines];
CTFrameGetLineOrigins(frame, CFRangeMake(0, 0), lineOrigins);
//總的高度
CTLineRef line = CFArrayGetValueAtIndex(lines, numOfLines-1);
self.fullHeight = lineOrigins[0].y-lineOrigins[numOfLines-1].y+CTLineGetBoundsWithOptions(line,kCTLineBoundsExcludeTypographicLeading).size.height;
//如果超過指定行數(shù)迁霎,就計算部分高度
if (numOfLines > self.foldLineThreshold) {
CTLineRef line = CFArrayGetValueAtIndex(lines, self.foldLineCount-1);
self.foldHeight = lineOrigins[0].y-lineOrigins[self.foldLineCount-1].y+CTLineGetBoundsWithOptions(line,kCTLineBoundsExcludeTypographicLeading).size.height;
self.foldFlag = 1;
} else {
self.foldFlag = 0;
}
//用完了記得釋放哦
CFRelease(framesetter);
CFRelease(frame);
CoreText實現(xiàn)
看上圖,我們可以知道百宇,一個View包括CTFrame
考廉,CTFrame
中間包括許多行CTLine
,而一個CTLine
中包括許多CTRun
我們主要說說CTLine
和CTRun
CTLine
其實比較好理解携御,當我們用UILabel
顯示文本內容時昌粤,一行就是一個CTLine
既绕,一個CTLine
中有一個或者多個CTRun
,CTRun
的個數(shù)取決于文本的具體內容涮坐,如果一行里面有兩種大小的文字或者兩種顏色不通的文字凄贩,可以簡單的認為這一行就有兩個CTRun
,其中CTRun
還可以是圖片
我們再來說說文字結構
Origin是原點袱讹,后面獲取的坐標都是該坐標疲扎,寬度是可以直接獲取的,而高度則是由Ascent和Descent相加得到
具體實現(xiàn)
具體實現(xiàn)過程中主要用到了NSMutableAttributedString
中的Attributes
NSMutableAttributedString *astring = _textString;
//獲取上下文
CGContextRef context = UIGraphicsGetCurrentContext();
//設置坐標系
//設置字形的變換矩陣為不做圖形變換
CGContextSetTextMatrix(context, CGAffineTransformIdentity);
//平移方法捷雕,將畫布向上平移一個屏幕高度
CGContextTranslateCTM(context, 0, self.bounds.size.height);
//縮放方法椒丧,x軸縮放系數(shù)為1,則不變救巷,y軸縮放系數(shù)為-1壶熏,則相當于以x軸為軸旋轉180度
CGContextScaleCTM(context, 1, -1);
//創(chuàng)建path
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddRect(path, NULL, self.bounds);
CTFramesetterRef frameRef = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)astring);
CTFrameRef frame = CTFramesetterCreateFrame(frameRef, CFRangeMake(0, astring.length), path, NULL);
CTFrameDraw(frame, context);
CFRelease(path);
CFRelease(frame);
CFRelease(frameRef);
首先是獲取上下文
由于屏幕的坐標系是原點在左上角,然后x浦译,y軸的箭頭方向分別是向右和向下棒假,與Quartz 2D
坐標系不一樣,Quartz 2D
坐標系是原點在左下角精盅,x帽哑,y軸的方向分別是向右和向上,CGContextSetTextMatrix () ,CGContextTranslateCTM (), CGContextScaleCTM ()
這三句主要就是轉換坐標系叹俏,感興趣的同學可以試著改改其中的參數(shù)
然后再創(chuàng)建path祝拯,這個path主要是為了后面的CTFrame
服務
根據(jù)文本內容創(chuàng)建CTFramesetterRef
根據(jù)CTFramesetterRef
Range
path
來得到CTFrameRef
最后通過CTFrameDraw
來將CTFrameRef
繪制在上下文中
因為這些都是C方法,所以要自己來釋放她肯,通過CFRelease
來釋放
加這段代碼放到View的drawRect
中就可以運行了.