- (CGSize) measureFrame: (CTFrameRef) frame forContext: (CGContext *) cgContext {//frame為排版後的文本 CGPathRefframePath =CTFrameGetPath(frame); CGRectframeRect =CGPathGetBoundingBox(framePath); CFArrayReflines =CTFrameGetLines(frame); CFIndexnumLines =CFArrayGetCount(lines); CGFloatmaxWidth =0; CGFloattextHeight =0; // Now run through each line determining the maximum width of all the lines. // We special case the last line of text. While we've got it's descent handy, // we'll use it to calculate the typographic height of the text as well. CFIndexlastLineIndex = numLines -1; for(CFIndexindex =0; index < numLines; index++) { CGFloatascent, descent, leading, width; CTLineRefline = (CTLineRef)CFArrayGetValueAtIndex(lines, index); width =CTLineGetTypographicBounds(line, &ascent, &descent, &leading); if(width > maxWidth) { maxWidth = width; } if(index == lastLineIndex) { // Get the origin of the last line. We add the descent to this // (below) to get the bottom edge of the last line of text. CGPointlastLineOrigin; CTFrameGetLineOrigins(frame,CFRangeMake(lastLineIndex,1), &lastLineOrigin); // The height needed to draw the text is from the bottom of the last line // to the top of the frame. textHeight = CGRectGetMaxY(frameRect) - lastLineOrigin.y+ descent; } } // For some text the exact typographic bounds is a fraction of a point too // small to fit the text when it is put into a context. We go ahead and round // the returned drawing area up to the nearest point. This takes care of the // discrepencies. returnCGSizeMake(ceil(maxWidth),ceil(textHeight)); }使用Core Text計算一段文本繪制在屏幕上之後的高度 Core Text提供了一系列方便的函數,可以很容易的把文本繪制在屏幕上,對於一個Frame來說,一般並不需要擔心文本的排列問題,這些Core Text的函數都可以直接搞定,只要給他一個大小合適的CGRect就可以。
但,在某些情況下,我們還希望知道這段文本在繪制之後,對應繪制的字體字號設置,在屏幕上實際占用了多大面積。舉例來說,有文本段落a,屏幕大小rect,通常做法是以rect創建path,然後創建CTFramesetter,再然後創建CTFrame,最後用CTFrameDraw畫出來,這時候,往往文本段落占用的實際面積會小於rect,這時候就有必要獲得這段文本所占用的真正面積。
最理想的情況是使用
double CTLineGetTypographicBounds( CTLineRef line, CGFloat* ascent, CGFloat* descent, CGFloat* leading );
這是Core Text提供的函數,傳入CTLine,就會得到這一行的ascent,descent和leading,在OSX上通常可以工作的很好,但是在iOS(iPhone/iPad)上這個函數的結果略有不同。
正常情況下,計算行高只需要ascent+descent+leading即可。在這個略有不同的情況下,leading的值會出現偏差,導致算出來的結果是錯誤的。如果不管行距,ascent+descent計算出來的Glyph的高度還是正確的。
這樣就有了第一步
在創建用於繪圖的CFAttributedStringRef時,除了設置字體,多設置一個CTParagraphStyleRef,其中特別應該確定行距kCTParagraphStyleSpecifierLineSpacing。在計算這裡時,先逐行計算ascent+descent,累加起來,再加上一個行數*之前設置好的行距,這樣算出來的就是這些文本的實際高度,CTLineGetTypographicBounds返回的結果是寬度,這樣就可得到文本實際填充面積的Rect了。
但是這還是有問題,因為OSX上和iOS處理不同,所以事實上iOS的模擬器和真機的最終效果是不一樣的,這樣調試程序很麻煩。
於是還需要第二步
在最終往頁面上繪制的時候,不再用CTFrameDraw來一次繪制全部,而是使用CTLineDraw逐行畫,在畫之前,先用CGContextSetTextPosition來設置好每行文本的位置。這樣就保證了在任何平台上繪制效果一致。
問題就解決了。
CoreText在OS X和iOS上實現有很多細節區別,比如說,對CTRun的劃分方式也不一樣,在iOS上劃分出來的Run數量比OSX上少很多,是按照字體劃分的Run,按照文檔出來,這是正確的,但OSX上是一個字一個Run這樣劃分的,和文檔不符。iOS上的處理顯然效率更好。