CoreText2

blog.csdn.net

CoreText實現圖文混排 - 博客頻道

CoreText實現圖文混排

也好久沒來寫博客了,主要是最近也工作了,手頭的事有點多,一時間也就斷了,閑下來了我就來補博客了,剛好最近也做了很多東西,放在這里也算給自己做個筆記吧。

最近公司做了一個項目,需要用到圖文混排技術。于是呢就瘋狂地在網上搜刮資料。

不過很不幸的是,百度的CoreText資料還是比較少滴,翻來覆去就那幾個版本。

然而我又上不去谷歌,so,困難重重啊。

不過雖然資料少,不夠前輩們給的貢獻終于還是在我的努力下都被我消化了,然后我也來做個筆記。

CoreText的介紹

Core Text 是基于iOS3.2+ 和 OSX 10.5+ 的一種能夠對文本格式和文本布局進行精細控制的文本引擎。

它良好的結合了 UIKit 和 Core Graphics/Quartz:

UIKit 的 UILabel 允許你通過在 IB 中簡單的拖曳添加文本,但你不能改變文本的顏色和其中的單詞。? CoreGraphics/Quartz幾乎允許你做任何系統允許的事情,但你需要為每個字形計算位置,并畫在屏幕上。CoreText正結合了這兩者!你可以完全控制位置、布局、類似文本大小和顏色這樣的屬性,而 CoreText將幫你完善其它的東西——類似文本換行、字體呈現等等。

以上就是對CoreText的介紹。

老司機對CoreText實現圖文混排的一些理解

老司機認為,圖文混排中使用到的CoreText只是CoreText龐大體系中一個對富文本的增強的一部分。

我個人想法啊,我讀書少,理解的可能不到位,不過你咬我啊。

恩,我又逗逼了一波,說好的大師氣質呢,下面開始嚴肅了啊。

嚴肅的就是iOS7新推出的類庫Textkit,其實是在之前推出的CoreText上的封裝,根據蘋果的說法,他們開發了兩年多才完成,而且他們在開發時候也將表情混排作為一個使用案例進行研究,所以要實現表情混排將會非常容易。

蘋果引入TextKit的目的并非要取代已有的CoreText框架,雖然CoreText的主要作用也是用于文字的排版和渲染,但它是一種先進而又處于底層技術,如果我們需要將文本內容直接渲染到圖形上下文(Graphics context)時,從性能和易用性來考慮,最佳方案就是使用CoreText。

原理的東西學一學總沒有壞處。因此,還是有必要去學一學CoreText的。

那我們開始學習吧。

富文本

老司機說過,我要講的只是用來增強富文本的那一部分,那么富文本怎么使用呢。

富文本是什么呢?

富文本格式(RTF)規范是為了便于在應用程序之間輕松轉儲格式化文本和圖形的一種編碼方法?,F在,用戶可以利用特定轉換軟件,在不同系統如MS-DOS、Windows、OS/2、Macintosh和Power Macintosh的應用程序之間轉移字處理文檔。RTF規范提供一種在不同的輸出設備、操作環境和操作系統之間交換文本和圖形的一種格式。RTF使用ANSI, PC-8, Macintosh, 或IBM PC字符集控制文檔的表示法和格式化,包括屏幕顯示和打印。憑借RTF規范,不同的操作系統和不同的軟件程序創建的文檔能夠在這些操作系統和應用程序之間傳遞。將一個格式化的文件轉換為RTF文件的軟件稱為RTF書寫器。RTF書寫器用于分離現有文本中的程序控制信息,并且生成一個包含文本和與之相關的RTF組的新文件。將RTF文件轉換成格式化文件的軟件則稱為RTF閱讀器。

簡單的說,附帶有每一個文字屬性的字符串,就是富文本。

在iOS中,我們有一個專門的類來處理富文本AttributeString。

富文本的基本使用方法

誒,標題越來越小了,都4個#號了,說明我扯遠了啊。不過要想使用CoreText不會富文本還是不行啊。

來吧。

AttributedString也分為NSAttributedString和NSMutableAttributedString兩個類,類似于String,我就不贅述了。

富文本本質上沒有什么難度,只要給指定的字符串附上指定的屬性就好了。下面給出富文本的一些基本方法。

-initWithString:以NSString初始化一個富文本對象

-setAttributes:range:為富文本中的一段范圍添加一些屬性,第一個參數是個NSDictionary字典,第二個參數是NSRange。

-addAttribute:value:range:添加一個屬性

-addAttributes:range:添加多個屬性

-removeAttribute:range:移除屬性

額,老司機知道這么說不直觀,來來來,上代碼。

NSDictionary* dic = @{NSFontAttributeName:[UIFontfontWithName:@"Zapfino"size:20],NSForegroundColorAttributeName:[UIColorredColor],NSUnderlineStyleAttributeName:@(NSUnderlineStyleSingle)};NSMutableAttributedString* attributeStr = [[NSMutableAttributedStringalloc] initWithString:@"0我是一個富文本,9聽說我有很多屬性,19I will try。32這里清除屬性."];? ? [attributeStr setAttributes:dic range:NSMakeRange(0, attributeStr.length)];? ? [attributeStr addAttribute:NSFontAttributeNamevalue:[UIFontsystemFontOfSize:30] range:NSMakeRange(9,10)];? ? [attributeStr addAttribute:NSForegroundColorAttributeNamevalue:[UIColorcyanColor] range:NSMakeRange(13,13)];NSDictionary* dicAdd = @{NSBackgroundColorAttributeName:[UIColoryellowColor],NSLigatureAttributeName:@1};? ? [attributeStr addAttributes:dicAdd range:NSMakeRange(19,13)];? ? [attributeStr removeAttribute:NSFontAttributeNamerange:NSMakeRange(32,9)];UILabel* label = [[UILabelalloc] initWithFrame:CGRectMake(100,100,200,400)];? ? label.numberOfLines=0;? ? label.attributedText= attributeStr;

這里你要注意一下,給label的一定是給他的attributedText屬性,你給text是不行的。

是不是用起來很簡單,富文本,跟字典沒什么區別么。

CoreText繪制富文本

是不是終于進入正題了。其實之所以說那么多,還是為了你看完就能保證會用啊,否則你不會富文本你自己還要查找富文本相關資料。

Come On!

CoreText實現圖文混排其實就是在富文本中插入一個空白的圖片占位符的富文本字符串,通過代理設置相關的圖片尺寸信息,根據從富文本得到的frame計算圖片繪制的frame再繪制圖片這么一個過程。

先來整體代碼

-(void)drawRect:(CGRect)rect{? ? [superdrawRect:rect];CGContextRefcontext =UIGraphicsGetCurrentContext();CGContextSetTextMatrix(context,CGAffineTransformIdentity);CGContextTranslateCTM(context,0,self.bounds.size.height);CGContextScaleCTM(context,1.0, -1.0);NSMutableAttributedString* attributeStr = [[NSMutableAttributedStringalloc] initWithString:@"\n這里在測試圖文混排,\n我是一個富文本"];? ? CTRunDelegateCallbacks callBacks;? ? callBacks.version= kCTRunDelegateVersion1;? ? callBacks.getAscent= ascentCallBacks;? ? callBacks.getDescent= descentCallBacks;? ? callBacks.getWidth= widthCallBacks;NSDictionary* dicPic = @{@"height":@129,@"width":@400};? ? CTRunDelegateRef delegate = CTRunDelegateCreate(& callBacks, (__bridgevoid*)dicPic);unicharplaceHolder =0xFFFC;NSString* placeHolderStr = [NSStringstringWithCharacters:&placeHolder length:1];NSMutableAttributedString* placeHolderAttrStr = [[NSMutableAttributedStringalloc] initWithString:placeHolderStr];CFAttributedStringSetAttribute((CFMutableAttributedStringRef)placeHolderAttrStr,CFRangeMake(0,1), kCTRunDelegateAttributeName, delegate);CFRelease(delegate);? ? [attributeStr insertAttributedString:placeHolderAttrStr atIndex:12];? ? CTFramesetterRef frameSetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attributeStr);CGMutablePathRefpath =CGPathCreateMutable();CGPathAddRect(path,NULL,self.bounds);NSIntegerlength = attributeStr.length;? ? CTFrameRef frame = CTFramesetterCreateFrame(frameSetter,CFRangeMake(0, length), path,NULL);? ? CTFrameDraw(frame, context);UIImage* image = [UIImageimageNamed:@"bd_logo1"];CGRectimgFrm = [selfcalculateImageRectWithFrame:frame];CGContextDrawImage(context,imgFrm, image.CGImage);CFRelease(frame);CFRelease(path);}staticCGFloatascentCallBacks(void* ref){return[(NSNumber*)[(__bridgeNSDictionary*)ref valueForKey:@"height"] floatValue];}staticCGFloatdescentCallBacks(void* ref){return0;}staticCGFloatwidthCallBacks(void* ref){return[(NSNumber*)[(__bridgeNSDictionary*)ref valueForKey:@"width"] floatValue];}-(CGRect)calculateImageRectWithFrame:(CTFrameRef)frame{NSArray* arrLines = (NSArray*)CTFrameGetLines(frame);NSIntegercount = [arrLines count];CGPointpoints[count];? ? CTFrameGetLineOrigins(frame,CFRangeMake(0,0), points);for(inti =0; i < count; i ++) {? ? ? ? CTLineRef line = (__bridge CTLineRef)arrLines[i];NSArray* arrGlyphRun = (NSArray*)CTLineGetGlyphRuns(line);for(intj =0; j < arrGlyphRun.count; j ++) {? ? ? ? ? ? CTRunRef run = (__bridge CTRunRef)arrGlyphRun[j];NSDictionary* attributes = (NSDictionary*)CTRunGetAttributes(run);? ? ? ? ? ? CTRunDelegateRef delegate = (__bridge CTRunDelegateRef)[attributes valueForKey:(id)kCTRunDelegateAttributeName];if(delegate ==nil) {continue;? ? ? ? ? ? }NSDictionary* dic = CTRunDelegateGetRefCon(delegate);if(![dic isKindOfClass:[NSDictionaryclass]]) {continue;? ? ? ? ? ? }CGPointpoint = points[i];CGFloatascent;CGFloatdescent;CGRectboundsRun;? ? ? ? ? ? boundsRun.size.width= CTRunGetTypographicBounds(run,CFRangeMake(0,0), &ascent, &descent,NULL);? ? ? ? ? ? boundsRun.size.height= ascent + descent;CGFloatxOffset = CTLineGetOffsetForStringIndex(line, CTRunGetStringRange(run).location,NULL);? ? ? ? ? ? boundsRun.origin.x= point.x+ xOffset;? ? ? ? ? ? boundsRun.origin.y= point.y- descent;CGPathRefpath = CTFrameGetPath(frame);CGRectcolRect =CGPathGetBoundingBox(path);CGRectimageBounds =CGRectOffset(boundsRun, colRect.origin.x, colRect.origin.y);returnimageBounds;? ? ? ? }? ? }returnCGRectZero;}

不瞞你說,我看著代碼都煩,也怕,所以放心,老司機會一句一句給你解釋的。

分段解析

準備工作

CGContextRefcontext =UIGraphicsGetCurrentContext();CGContextSetTextMatrix(context,CGAffineTransformIdentity);CGContextTranslateCTM(context,0,self.bounds.size.height);CGContextScaleCTM(context,1.0, -1.0);

先要來一個背景介紹哈

/*

coreText 起初是為OSX設計的,而OSX得坐標原點是左下角,y軸正方向朝上。iOS中坐標原點是左上角,y軸正方向向下。

若不進行坐標轉換,則文字從下開始,還是倒著的

如下圖(盜的圖,別打我)

*/

系統坐標系

這四句什么意思呢?

首先第一句。

CGContextRefcontext =UIGraphicsGetCurrentContext();

為什么要回去上下文呢?因為我們所有的繪制操作都是在上下文上進行繪制的。

然后剩下的三句。

CGContextSetTextMatrix(context,CGAffineTransformIdentity);CGContextTranslateCTM(context,0,self.bounds.size.height);CGContextScaleCTM(context,1.0, -1.0);

正如之上的背景說的,coreText使用的是系統坐標,然而我們平時所接觸的iOS的都是屏幕坐標,所以要將屏幕坐標系轉換系統坐標系,這樣才能與我們想想的坐標互相對應。

事實上呢,這三句是翻轉畫布的固定寫法,這三句你以后會經常看到的。

繼續。

圖片的代理的設置

/*

事實上,圖文混排就是在要插入圖片的位置插入一個富文本類型的占位符。通過CTRUNDelegate設置圖片

*/

NSMutableAttributedString* attributeStr = [[NSMutableAttributedStringalloc] initWithString:@"\n這里在測試圖文混排,\n我是一個富文本"];

CTRunDelegateCallbacks callBacks;callBacks.version= kCTRunDelegateVersion1;callBacks.getAscent= ascentCallBacks;callBacks.getDescent= descentCallBacks;callBacks.getWidth= widthCallBacks;

為什么要設置一個回調結構體呢?

因為coreText中大量的調用c的方法。事實上你會發現大部分跟系統底層有關的都需要調c的方法。所以設置代理要按照人家的方法來啊。

看看這幾句代碼也很好懂,就是注釋中寫的意思。

后三句分別就是說當我需要走這些代理的時候都會走那些代理方法。

好吧,扯到這又要補充知識了。這個距離什么東西呢?

字形

對對,這呢就是一個CTRun的尺寸圖,什么你問CTRun是啥?還沒到那呢,后面會詳細介紹。

在這你只要知道,一會我們繪制圖片的時候實際上實在一個CTRun中繪制這個圖片,那么CTRun繪制的坐標系中,他會以origin點作為原點進行繪制。

基線為過原點的x軸,ascent即為CTRun頂線距基線的距離,descent即為底線距基線的距離。

我們繪制圖片應該從原點開始繪制,圖片的高度及寬度及CTRun的高度及寬度,我們通過代理設置CTRun的尺寸間接設置圖片的尺寸。

NSDictionary * dicPic = @{@"height":@129,@"width":@400};? ? CTRunDelegateRef delegate = CTRunDelegateCreate(& callBacks, (__bridgevoid*)dicPic);

上面只是設置了回調結構體,然而我們還沒有告訴這個代理我們要的圖片尺寸。

所以這句話就在設置代理的時候綁定了一個返回圖片尺寸的字典。

事實上此處你可以綁定任意對象。此處你綁定的對象既是回調方法中的參數ref。

好吧就然說到這我就直接把那三個回調方法說了吧,放在一起比較好理解一些。

staticCGFloatascentCallBacks(void*ref){return[(NSNumber *)[(__bridge NSDictionary *)refvalueForKey:@"height"] floatValue];}staticCGFloatdescentCallBacks(void*ref){return0;}staticCGFloatwidthCallBacks(void*ref){return[(NSNumber *)[(__bridge NSDictionary *)refvalueForKey:@"width"] floatValue];}

上文說過,ref既是創建代理是綁定的對象。所以我們在這里,從字典中分別取出圖片的寬和高。

值得注意的是,由于是c的方法,所以也沒有什么對象的概念。是一個指針類型的數據。不過oc的對象其實也就是c的結構體。我們可以通過類型轉換獲得oc中的字典。

__bridge既是C的結構體轉換成OC對象時需要的一個修飾詞。

老司機敲字慢啊,敲到這都兩個小時了,容我喝口水。

你們喝過紅色的尖叫么?老司機喝了那種煙頭泡的水之后精神滿滿的繼續敲字。(那水超難喝,你可以挑戰一下)

誒,說好的嚴肅呢?

圖片的插入

首先創建一個富文本類型的圖片占位符,綁定我們的代理

unicharplaceHolder =0xFFFC;NSString* placeHolderStr = [NSStringstringWithCharacters:&placeHolder length:1];NSMutableAttributedString* placeHolderAttrStr = [[NSMutableAttributedStringalloc] initWithString:placeHolderStr];CFAttributedStringSetAttribute((CFMutableAttributedStringRef)placeHolderAttrStr,CFRangeMake(0,1), kCTRunDelegateAttributeName, delegate);CFRelease(delegate);

這里富文本的知識上文中已經介紹過了。不過老司機猜你有三個疑問。

這個添加屬性的方法怎么是這個樣子的?

因為這里是添加CTRunDelegate這種數據類型,要用CoreText專門的方法,不過其實就是形式不同,作用一樣的。

為什么這里富文本類型轉換的時候不用_bridge呢?老司機你不是說需要修飾詞么?你是不是騙我?(markDown語法沖突我少打一個下劃線)

真沒有,事實上不是所有數據轉換的時候都需要__bridge。你要問我怎么區分?那好我告訴你,C中就是傳遞指針的數據就不用。比如說字符串,數組。原因老司機現在解釋不通,等我能組織好語言的。

為什么還要釋放?我是ARC環境啊

不好意思,我也是。不過為什么要釋放呢?因為你進行了類型轉換之后就不屬于對象了,也不再歸自動引用計數機制管理了,所以你得手動管理咯。

然后將占位符插入到我們的富文本中

[attributeStr insertAttributedString:placeHolderAttrStr atIndex:12];

此處我就不贅述了,富文本的知識你只要類比字典就好了。

至此,我們已經生成好了我們要的帶有圖片信息的富文本了,接下來我們只要在畫布上繪制出來這個富文本就好了。

繪制

繪制呢,又分成兩部分,繪制文本和繪制圖片。你問我為什么還分成兩個?

因為富文本中你添加的圖片只是一個帶有圖片尺寸的空白占位符啊,你繪制的時候他只會繪制出相應尺寸的空白占位符,所以什么也顯示不了啊。

那怎么顯示圖片???拿到占位符的坐標,在占位符的地方繪制相應大小的圖片就好了。恩,說到這,圖文混排的原理已經說完了。

先來繪制文本吧。

繪制文本

CTFramesetterRef frameSetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attributeStr);CGMutablePathRefpath =CGPathCreateMutable();CGPathAddRect(path,NULL,self.bounds);NSIntegerlength = attributeStr.length;CTFrameRef frame = CTFramesetterCreateFrame(frameSetter,CFRangeMake(0,length), path,NULL);CTFrameDraw(frame, context);

frameSetter是根據富文本生成的一個frame生成的工廠,你可以通過framesetter以及你想要繪制的富文本的范圍獲取該CTRun的frame。

但是你需要注意的是,獲取的frame是僅繪制你所需要的那部分富文本的frame。即當前情況下,你繪制范圍定為(10,1),那么你得到的尺寸是只繪制(10,1)的尺寸,他應該從屏幕左上角開始(因為你改變了坐標系),而不是當你繪制全部富文本時他該在的位置。

然后建立一會繪制的尺寸,實際上就是在指定你的繪制范圍。

接著生成整個富文本繪制所需要的frame。因為范圍是全部文本,所以獲取的frame即為全部文本的frame(此處老司機希望你一定要搞清楚全部與指定范圍獲取的frame他們都是從左上角開始的,否則你會進入一個奇怪的誤區,稍后會提到的)。

最后,根據你獲得的frame,繪制全部富文本。

繪制圖片

上面你已經繪制出文字,不過沒有圖片哦,接下來繪制圖片。

繪制圖片用下面這個方法,通用的哦

CGContextDrawImage(context,imgFrm, image.CGImage);

我們可以看到這個方法有三個參數,分別是context,frame,以及image。

要什么就給他什么好咯,context和image都好說,context就是當前的上下文,最開始獲得那個。image就是你要添加的那個圖片,不過是CGImage類型。通過UIImage轉出CGImage就好了,我們重點講一下frame的獲取。

frame的獲取

記得我之前說的誤區么?這里我們要獲得Image的frame,你有沒有想過我們的frameSetter?

我也想過,不過就像我說的,你單獨用frameSetter求出的image的frame是不正確的,那是只繪制image而得的坐標,所以哪種方法不能用哦,要用下面的方法。

你們一定發現,我獲取frame的方法單獨寫了一個方法,為什么呢?

1.將代碼分離,方便修改。

2.最主要的是這部分代碼到哪里都能用,達到復用效果。

NSArray* arrLines = (NSArray*)CTFrameGetLines(frame);NSIntegercount = [arrLines count];CGPointpoints[count];CTFrameGetLineOrigins(frame,CFRangeMake(0,0), points);

第一句呢,獲取繪制frame中的所有CTLine。CTLine,又不知道了吧,老司機又要無恥的盜圖了。

CTFrame組成

上面呢,我們能看到一個CTFrame繪制的原理。

CTLine 可以看做Core Text繪制中的一行的對象 通過它可以獲得當前行的line ascent,line descent ,line leading,還可以獲得Line下的所有Glyph Runs

CTRun 或者叫做 Glyph Run,是一組共享想相同attributes(屬性)的字形的集合體

一個CTFrame有幾個CTLine組成,有幾行文字就有幾行CTLine。一個CTLine有包含多個CTRun,一個CTRun是所有屬性都相同的那部分富文本的繪制單元。所以CTRun是CTFrame的基本繪制單元。

接著說我們的代碼。

為什么我獲取的數組需要進行類型轉換呢?因為CTFrameGetLines()返回值是CFArrayRef類型的數據。就是一個c的數組類型吧,暫且先這么理解,所以需要轉換。

那為什么不用__bridge呢?記得么,我說過,本身就傳地址的數據是不用橋接的。就是這樣。

然后獲取數組的元素個數。有什么用呢,因為我們要用到每個CTLine的原點坐標進行計算。每個CTLine都有自己的origin。所以要生成一個相同元素個數的數組去盛放origin對象。

然后用CTFrameGetLineOrigins獲取所有原點。

到此,我們計算frame的準備工作完成了。才完成準備工作。

計算frame

思路呢,就是遍歷我們的frame中的所有CTRun,檢查他是不是我們綁定圖片的那個,如果是,根據該CTRun所在CTLine的origin以及CTRun在CTLine中的橫向偏移量計算出CTRun的原點,加上其尺寸即為該CTRun的尺寸。

跟繞口令是的,不過就是這么個思路。

for(inti =0; i < count; i ++) {? ? ? ? CTLineRef line = (__bridge CTLineRef)arrLines[i];NSArray* arrGlyphRun = (NSArray*)CTLineGetGlyphRuns(line);for(intj =0; j < arrGlyphRun.count; j ++) {? ? ? ? ? ? CTRunRef run = (__bridge CTRunRef)arrGlyphRun[j];NSDictionary* attributes = (NSDictionary*)CTRunGetAttributes(run);? ? ? ? ? ? CTRunDelegateRef delegate = (__bridge CTRunDelegateRef)[attributes valueForKey:(id)kCTRunDelegateAttributeName];if(delegate ==nil) {continue;? ? ? ? ? ? }NSDictionary* dic = CTRunDelegateGetRefCon(delegate);if(![dic isKindOfClass:[NSDictionaryclass]]) {continue;? ? ? ? ? ? }CGPointpoint = points[i];CGFloatascent;CGFloatdescent;CGRectboundsRun;? ? ? ? ? ? boundsRun.size.width= CTRunGetTypographicBounds(run,CFRangeMake(0,0), &ascent, &descent,NULL);? ? ? ? ? ? boundsRun.size.height= ascent + descent;CGFloatxOffset = CTLineGetOffsetForStringIndex(line, CTRunGetStringRange(run).location,NULL);? ? ? ? ? ? boundsRun.origin.x= point.x+ xOffset;? ? ? ? ? ? boundsRun.origin.y= point.y- descent;CGPathRefpath = CTFrameGetPath(frame);CGRectcolRect =CGPathGetBoundingBox(path);CGRectimageBounds =CGRectOffset(boundsRun, colRect.origin.x, colRect.origin.y);returnimageBounds;

有了上面的思路這里就很好理解了。

外層for循環呢,是為了取到所有的CTLine。

類型轉換什么的我就不多說了,然后通過CTLineGetGlyphRuns獲取一個CTLine中的所有CTRun。

里層for循環是檢查每個CTRun。

通過CTRunGetAttributes拿到該CTRun的所有屬性。

通過kvc取得屬性中的代理屬性。

接下來判斷代理屬性是否為空。因為圖片的占位符我們是綁定了代理的,而文字沒有。以此區分文字和圖片。

如果代理不為空,通過CTRunDelegateGetRefCon取得生成代理時綁定的對象。判斷類型是否是我們綁定的類型,防止取得我們之前為其他的富文本綁定過代理。

如果兩條都符合,ok,這就是我們要的那個CTRun。

開始計算該CTRun的frame吧。

獲取原點和獲取寬高被。

通過CTRunGetTypographicBounds取得寬,ascent和descent。有了上面的介紹我們應該知道圖片的高度就是ascent+descent了吧。

接下來獲取原點。

CTLineGetOffsetForStringIndex獲取對應CTRun的X偏移量。

取得對應CTLine的原點的Y,減去圖片的下邊距才是圖片的原點,這點應該很好理解。

至此,我們已經獲得了圖片的frame了。因為只綁定了一個圖片,所以直接return就好了,如果多張圖片可以繼續遍歷返回數組。

獲取到圖片的frame,我們就可以繪制圖片了,用上面介紹的方法。

哦,別忘了手動釋放你創建的對象哦。

CFRelease(frame)CFRelease(path)CFRelease(frameSetter)

大功告成。

好了,至此你已經完成圖片的繪制了。只要在ViewController里面引入你繪制CoreText文本的View正常的初始化添加子視圖就可以了。

好吧,這個教程我也是綜合了很多資料寫出來的。優勢是在于我一句一句講的,幾乎每一句都告訴你原理了吧。

恩,我也是在前人的基礎上自己總結查閱出來的,難免夾雜著個人理解和部分偏頗,如果各位看官發現我寫的有什么不對的地方歡迎與我聯系,老司機的郵箱codewicky@163.com。

原諒老司機逗逼的本質,嚴肅不起來。

下面是一些參考資料:

coreText方法,列的很全

CTRun的詳細介紹

CTLine的詳細介紹

coreText基本原理及使用方法

圖文混排的整體介紹

最后,你問我為什么一直叫自己老司機?哦,因為嘿嘿嘿~~~

哦,最后的最后,若果真有人轉載的話,麻煩你注明出處。

http://www.lxweimin.com/p/6db3289fb05d

文/老司機Wicky(簡書作者)

原文鏈接:http://www.lxweimin.com/p/6db3289fb05d

著作權歸作者所有,轉載請聯系作者獲得授權,并標注“簡書作者”。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,002評論 6 542
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,400評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,136評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,714評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,452評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,818評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,812評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,997評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,552評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,292評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,510評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,035評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,721評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,121評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,429評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,235評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,480評論 2 379

推薦閱讀更多精彩內容

  • 系列文章: CoreText實現圖文混排 CoreText實現圖文混排之點擊事件 CoreText實現圖文混排之文...
    老司機Wicky閱讀 40,254評論 221 432
  • 最近在網上看了一些大牛的文章,自己也試著寫了一下,感覺圖文混排真的很強大。 廢話不多說,開始整 先上效果圖跟代碼,...
    AllureJM閱讀 1,008評論 0 1
  • CoreText是iOS/OSX中文本顯示的一個底層框架,它是用C語言寫成的,有快速簡單的優勢。iOS中的Text...
    小貓仔閱讀 5,010評論 2 9
  • 1.iOS中的round、ceil、floor函數略解 round如果參數是小數,則求本身的四舍五入.ceil如果...
    K_Gopher閱讀 1,198評論 1 0
  • CoreText是一個進階的比較底層的布局文本和處理字體的技術,CoreText API在OS X v10.5 和...
    smalldu閱讀 13,529評論 18 129