CoreText 淺析

最近項目有這樣一個需求氮发,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)

image

看上圖,我們可以知道百宇,一個View包括CTFrame考廉,CTFrame中間包括許多行CTLine,而一個CTLine中包括許多CTRun

我們主要說說CTLineCTRun

CTLine其實比較好理解携御,當我們用UILabel顯示文本內容時昌粤,一行就是一個CTLine既绕,一個CTLine中有一個或者多個CTRunCTRun的個數(shù)取決于文本的具體內容涮坐,如果一行里面有兩種大小的文字或者兩種顏色不通的文字凄贩,可以簡單的認為這一行就有兩個CTRun,其中CTRun還可以是圖片

我們再來說說文字結構
image

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中就可以運行了.

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末佳头,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子晴氨,更是在濱河造成了極大的恐慌康嘉,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件籽前,死亡現(xiàn)場離奇詭異亭珍,居然都是意外死亡,警方通過查閱死者的電腦和手機枝哄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進店門肄梨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人挠锥,你說我怎么就攤上這事众羡。” “怎么了蓖租?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵粱侣,是天一觀的道長羊壹。 經常有香客問我,道長齐婴,這世上最難降的妖魔是什么油猫? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮柠偶,結果婚禮上情妖,老公的妹妹穿的比我還像新娘。我一直安慰自己诱担,他們只是感情好毡证,可當我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著该肴,像睡著了一般情竹。 火紅的嫁衣襯著肌膚如雪藐不。 梳的紋絲不亂的頭發(fā)上匀哄,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天,我揣著相機與錄音雏蛮,去河邊找鬼涎嚼。 笑死,一個胖子當著我的面吹牛挑秉,可吹牛的內容都是我干的法梯。 我是一名探鬼主播,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼犀概,長吁一口氣:“原來是場噩夢啊……” “哼立哑!你這毒婦竟也來了?” 一聲冷哼從身側響起姻灶,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤铛绰,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后产喉,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體捂掰,經...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年曾沈,在試婚紗的時候發(fā)現(xiàn)自己被綠了这嚣。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡塞俱,死狀恐怖姐帚,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情障涯,我是刑警寧澤卧土,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布惫皱,位于F島的核電站,受9級特大地震影響尤莺,放射性物質發(fā)生泄漏旅敷。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一颤霎、第九天 我趴在偏房一處隱蔽的房頂上張望媳谁。 院中可真熱鬧,春花似錦友酱、人聲如沸晴音。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽锤躁。三九已至,卻和暖如春或详,著一層夾襖步出監(jiān)牢的瞬間系羞,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工霸琴, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留椒振,地道東北人。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓梧乘,卻偏偏與公主長得像澎迎,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子选调,可洞房花燭夜當晚...
    茶點故事閱讀 44,592評論 2 353