使用CoreText繪制文本

CoreText


CoreText是底層的API弛房,它使用了許多C的函數(shù)(例如CTFramesetterCreateWithAttributedString傀广,CTFramesetterCreateFrame)來代替OC的類和方法。
Core Text是和Core Graphics配合使用的庸诱,一般是在UIView的drawRect方法中的Graphics Context上進(jìn)行繪制的捻勉。Core Text真正負(fù)責(zé)繪制的是文本部分隘膘,如果要繪制圖片,可以使用CoreText給圖片預(yù)留出位置撮奏,然后用Core Graphics繪制俏讹。demo地址

CoreText布局會用到attributed strings(CFAttributedStringRef)和graphics paths(CGPathRef)。attributed strings封裝了文本的屬性畜吊,例如字體和顏色泽疆;graphics paths定義了文本框的外形。

字形度量

字形度量就是字形的各個參數(shù):

ios_coretext_glyphs_1.jpg
glyph_metrics_2x.png
  • bounding box(邊界框)玲献,這是一個假想的框子殉疼,它盡可能緊密的裝入字形梯浪。

  • baseline(基線),一條假想的線,一行上的字形都以此線作為上下位置的參考瓢娜,在這條線的左側(cè)存在一個點(diǎn)叫做基線的原點(diǎn)挂洛。

  • ascent(上行高度),從原點(diǎn)到字體中最高(這里的高深都是以基線為參照線的)的字形的頂部的距離眠砾,ascent是一個正值抹锄。

  • descent(下行高度),從原點(diǎn)到字體中最深的字形底部的距離荠藤,descent是一個負(fù)值(比如一個字體原點(diǎn)到最深的字形的底部的距離為2伙单,那么descent就為-2)。

  • linegap(行距)哈肖,linegap也可以稱作leading(其實(shí)準(zhǔn)確點(diǎn)講應(yīng)該叫做External leading)吻育。

  • leading,文檔說的很含糊淤井,其實(shí)是上一行字符的descent到- 下一行的ascent之間的距離布疼。

  • 所以字體的高度是由三部分組成的:leading + ascent + descent。

字形和字符币狠,一些Metrics專業(yè)知識還可以參考Free Type的文檔 Glyph metrics游两,其實(shí)iOS就是使用Free Type庫來進(jìn)行字體渲染的。蘋果文檔 Querying Font Metrics 漩绵,Text Layout贱案。

CoreText對象模型

65cc0af7gw1e2uxd1gmhwj.jpg
65cc0af7gw1e2uyn6r88oj.jpg
core_text_arch_2x.png

運(yùn)行時的Core Text對象形成一個層次結(jié)構(gòu),如圖1所示止吐。 這個層次結(jié)構(gòu)的頂部是framesetter對象(CTFramesetterRef)宝踪。 使用attributed stringgraphics path作為輸入,框架設(shè)置器會生成一個或多個文本框(CTFrameRef)碍扔。 每個CTFrame對象都代表一個段落瘩燥。

從圖中可以看到,我們首先通過CFAttributeString來創(chuàng)建CTFramaeSetter不同,然后再通過CTFrameSetter來創(chuàng)建CTFrame厉膀。
在CTFrame內(nèi)部,是由多個CTLine來組成的二拐,每個CTLine代表一行服鹅,每個CTLine是由多個CTRun來組成,每個CTRun代表一組顯示風(fēng)格一致的文本卓鹿。

  • CTFrameSetter: CTFrameSetter是通過CFAttributeString進(jìn)行初始化它負(fù)責(zé)根據(jù)path生產(chǎn)對應(yīng)的CTFrame;
  • CTFrame:CTFrame可以想象成畫布, 畫布的大小范圍由CGPath決定菱魔。CTFrame由很多CTLine組成。 CTFrame可以通過CTFrameDraw函數(shù)直接繪制到context上吟孙,我們可以在繪制之前澜倦,操作CTFrame中的CTline聚蝶,進(jìn)行一些參數(shù)的微調(diào);
  • CTLine: CTLine可以看做Core Text繪制中的一行的對象藻治,通過它可以獲得當(dāng)前行的line ascent, line descent, line heading,還可以獲得CTLine下的所有CTRun碘勉;
  • CTRun: CTRun是一組共享相同attributes的集合體;
    要繪制圖片桩卵,需要用CoreText的CTRun為圖片在繪制過程中留出空間验靡。這個設(shè)置要用到CTRunDelegate。我們可以在要顯示圖片的地方雏节,用一個特殊的空白字符代替胜嗓,用CTRunDelegate為其設(shè)置ascent,descent钩乍,width等參數(shù)辞州,這樣在繪制文本的時候就會把圖片的位置留出來,用CGContextDrawImage方法直接繪制出來就行了寥粹。

創(chuàng)建CTRunDelegate:

CTRunDelegateRef __nullable CTRunDelegateCreate(
    const CTRunDelegateCallbacks* callbacks,
    void * __nullable refCon ) 

創(chuàng)建CTRunDelegate需要兩個參數(shù)变过,一個是callbacks結(jié)構(gòu)體,還有一個是callbacks里的函數(shù)調(diào)用時需要傳入的參數(shù)涝涤。

typedef struct
{
    CFIndex                         version;
    CTRunDelegateDeallocateCallback dealloc;
    CTRunDelegateGetAscentCallback  getAscent;
    CTRunDelegateGetDescentCallback getDescent;
    CTRunDelegateGetWidthCallback   getWidth;
} CTRunDelegateCallbacks;

callbacks是一個結(jié)構(gòu)體媚狰,主要包含了返回當(dāng)前CTRun的ascent,descent和width函數(shù)阔拳。

代碼

自定義一個繼承自UIView的子類CoreTextView;在.m文件里引入頭文件CoreText/CoreText.h重寫drawRect方法:

void RunDelegateDeallocCallback( void* refCon ){
    
}

CGFloat RunDelegateGetAscentCallback( void *refCon ){
    NSString *imageName = (__bridge NSString *)refCon;
    CGFloat height = [UIImage imageNamed:imageName].size.height;
    return height;
}

CGFloat RunDelegateGetDescentCallback(void *refCon){
    return 0;
}

CGFloat RunDelegateGetWidthCallback(void *refCon){
    NSString *imageName = (__bridge NSString *)refCon;
    CGFloat width = [UIImage imageNamed:imageName].size.width;
    return width;
}



- (void)drawRect:(CGRect)rect{
    [super drawRect:rect];
    //得到當(dāng)前繪制畫布的上下文崭孤,用于將后續(xù)內(nèi)容繪制在畫布上
    CGContextRef context = UIGraphicsGetCurrentContext();
    
    //將坐標(biāo)系上下翻轉(zhuǎn)。對于底層的繪制引擎來說衫生,屏幕的左下角是坐標(biāo)原點(diǎn)(0裳瘪,0),而對于上層的UIKit來說罪针,屏幕的左上角是坐標(biāo)原點(diǎn),為了之后的坐標(biāo)系按UIKit來做黄伊,在這里做了坐標(biāo)系的上下翻轉(zhuǎn)泪酱,這樣底層和上層的(0,0)坐標(biāo)就是重合的了
    CGContextSetTextMatrix(context, CGAffineTransformIdentity);
    CGContextTranslateCTM(context, 0, self.bounds.size.height);
    CGContextScaleCTM(context, 1.0,-1.0);
   
    //創(chuàng)建繪制的區(qū)域,這里將UIView的bounds作為繪制區(qū)域
    CGMutablePathRef path = CGPathCreateMutable();
    CGPathAddRect(path, NULL, self.bounds);
    
    NSMutableAttributedString * attString = [[NSMutableAttributedString alloc] initWithString:@"海洋生物學(xué)家在太平洋里發(fā)現(xiàn)了一條與眾不同的鯨还最。一般藍(lán)鯨的“歌唱”頻率在十五到二十五赫茲墓阀,長須鯨子啊二十赫茲左右,而它的頻率在五十二赫茲左右拓轻。"];
    //設(shè)置字體
    [attString addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:24] range:NSMakeRange(0, 5)];
    [attString addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:13] range:NSMakeRange(6, 2)];
    [attString addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:38] range:NSMakeRange(8, attString.length - 8)];
    
    
    //設(shè)置文字顏色
    [attString addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:NSMakeRange(0, 11)];
    [attString addAttribute:NSForegroundColorAttributeName value:[UIColor blueColor] range:NSMakeRange(11, attString.length - 11)];

    
    NSString * imageName = @"jingyu";
    CTRunDelegateCallbacks callbacks;
    callbacks.version = kCTRunDelegateVersion1;
    callbacks.dealloc = RunDelegateDeallocCallback;
    callbacks.getAscent = RunDelegateGetAscentCallback;
    callbacks.getDescent = RunDelegateGetDescentCallback;
    callbacks.getWidth = RunDelegateGetWidthCallback;
    
    CTRunDelegateRef runDelegate = CTRunDelegateCreate(&callbacks, (__bridge void * _Nullable)(imageName));
    //空格用于給圖片留位置
    NSMutableAttributedString *imageAttributedString = [[NSMutableAttributedString alloc] initWithString:@" "];
     CFAttributedStringSetAttribute((CFMutableAttributedStringRef)imageAttributedString, CFRangeMake(0, 1), kCTRunDelegateAttributeName, runDelegate);
    CFRelease(runDelegate);
    [imageAttributedString addAttribute:@"imageName" value:imageName range:NSMakeRange(0, 1)];
    [attString insertAttributedString:imageAttributedString atIndex:1];
    CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attString);
    CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, attString.length), path, NULL);
    //把frame繪制到context里
    CTFrameDraw(frame, context);

    NSArray * lines = (NSArray *)CTFrameGetLines(frame);
    NSInteger lineCount = lines.count;
    CGPoint lineOrigins[lineCount];
    //拷貝frame的line的原點(diǎn)到數(shù)組lineOrigins里斯撮,如果第二個參數(shù)里的length是0,將會從開始的下標(biāo)拷貝到最后一個line的原點(diǎn)
    CTFrameGetLineOrigins(frame, CFRangeMake(0, 0), lineOrigins);
    
    for (int i = 0; i < lineCount; i++) {
        CTLineRef line = (__bridge CTLineRef)lines[I];
        NSArray * runs = (__bridge NSArray *)CTLineGetGlyphRuns(line);
        for (int j = 0; j < runs.count; j++) {
            CTRunRef run =  (__bridge CTRunRef)runs[j];
            NSDictionary * dic = (NSDictionary *)CTRunGetAttributes(run);
            CTRunDelegateRef delegate = (__bridge CTRunDelegateRef)[dic objectForKey:(NSString *)kCTRunDelegateAttributeName];
            if (delegate == nil) {
                continue;
            }
            NSString * imageName = [dic objectForKey:@"imageName"];
            UIImage * image = [UIImage imageNamed:imageName];
            CGRect runBounds;
            CGFloat ascent;
            CGFloat descent;
            runBounds.size.width = CTRunGetTypographicBounds(run, CFRangeMake(0, 0), &ascent, &descent, NULL);
            runBounds.size.height = ascent + descent;
            CFIndex index = CTRunGetStringRange(run).location;
            CGFloat xOffset = CTLineGetOffsetForStringIndex(line, index, NULL);
            runBounds.origin.x = lineOrigins[i].x + xOffset;
            runBounds.origin.y = lineOrigins[i].y;
            runBounds.size =image.size;
            CGContextDrawImage(context, runBounds, image.CGImage);
        }
    }
    //底層的Core Foundation對象由于不在ARC的管理下扶叉,需要自己維護(hù)這些對象的引用計數(shù)勿锅,最后要釋放掉帕膜。
    CFRelease(frame);
    CFRelease(path);
}

運(yùn)行后效果如下:

10.22.23.png

異步繪制

上面的drawRect方法是在主線程里調(diào)用的,如果繪制的過程比較耗時溢十,可能會阻塞主線程垮刹,這時候可以將會值得過程發(fā)到子線程里進(jìn)行,繪制完成后將context轉(zhuǎn)成位圖张弛,然后再把位圖在主線程里設(shè)置到view的layer里荒典。

- (void)drawRect:(CGRect)rect{
    [super drawRect:rect];
   //將繪制過程放入到后臺線程中
    dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0), ^{
        UIGraphicsBeginImageContext(rect.size);
        //得到當(dāng)前繪制畫布的上下文,用于將后續(xù)內(nèi)容繪制在畫布上
        CGContextRef context = UIGraphicsGetCurrentContext();
        
        //將坐標(biāo)系上下翻轉(zhuǎn)吞鸭。對于底層的繪制引擎來說寺董,屏幕的左下角是坐標(biāo)原點(diǎn)(0,0)刻剥,而對于上層的UIKit來說遮咖,屏幕的左上角是坐標(biāo)原點(diǎn),為了之后的坐標(biāo)系按UIKit來做透敌,在這里做了坐標(biāo)系的上下翻轉(zhuǎn)盯滚,這樣底層和上層的(0,0)坐標(biāo)就是重合的了
        CGContextSetTextMatrix(context, CGAffineTransformIdentity);
        CGContextTranslateCTM(context, 0, rect.size.height);
        CGContextScaleCTM(context, 1.0,-1.0);
        
        //創(chuàng)建繪制的區(qū)域,這里將UIView的bounds作為繪制區(qū)域
        CGMutablePathRef path = CGPathCreateMutable();
        CGPathAddRect(path, NULL, rect);
        
        NSMutableAttributedString * attString = [[NSMutableAttributedString alloc] initWithString:@"海洋生物學(xué)家在太平洋里發(fā)現(xiàn)了一條與眾不同的鯨酗电。一般藍(lán)鯨的“歌唱”頻率在十五到二十五赫茲魄藕,長須鯨子啊二十赫茲左右,而它的頻率在五十二赫茲左右撵术。"];
        //設(shè)置字體
        [attString addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:24] range:NSMakeRange(0, 5)];
        [attString addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:13] range:NSMakeRange(6, 2)];
        [attString addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:38] range:NSMakeRange(8, attString.length - 8)];
        
        
        //設(shè)置文字顏色
        [attString addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:NSMakeRange(0, 11)];
        [attString addAttribute:NSForegroundColorAttributeName value:[UIColor blueColor] range:NSMakeRange(11, attString.length - 11)];
        
        
        NSString * imageName = @"jingyu";
        CTRunDelegateCallbacks callbacks;
        callbacks.version = kCTRunDelegateVersion1;
        callbacks.dealloc = RunDelegateDeallocCallback;
        callbacks.getAscent = RunDelegateGetAscentCallback;
        callbacks.getDescent = RunDelegateGetDescentCallback;
        callbacks.getWidth = RunDelegateGetWidthCallback;
        
        CTRunDelegateRef runDelegate = CTRunDelegateCreate(&callbacks, (__bridge void * _Nullable)(imageName));
        //空格用于給圖片留位置
        NSMutableAttributedString *imageAttributedString = [[NSMutableAttributedString alloc] initWithString:@" "];
        CFAttributedStringSetAttribute((CFMutableAttributedStringRef)imageAttributedString, CFRangeMake(0, 1), kCTRunDelegateAttributeName, runDelegate);
        CFRelease(runDelegate);
        [imageAttributedString addAttribute:@"imageName" value:imageName range:NSMakeRange(0, 1)];
        [attString insertAttributedString:imageAttributedString atIndex:1];
        CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attString);
        CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, attString.length), path, NULL);
        //把frame繪制到context里
        CTFrameDraw(frame, context);
        
        NSArray * lines = (NSArray *)CTFrameGetLines(frame);
        NSInteger lineCount = lines.count;
        CGPoint lineOrigins[lineCount];
        //拷貝frame的line的原點(diǎn)到數(shù)組lineOrigins里背率,如果第二個參數(shù)里的length是0,將會從開始的下標(biāo)拷貝到最后一個line的原點(diǎn)
        CTFrameGetLineOrigins(frame, CFRangeMake(0, 0), lineOrigins);

        for (int i = 0; i < lineCount; i++) {
            CTLineRef line = (__bridge CTLineRef)lines[I];
            NSArray * runs = (__bridge NSArray *)CTLineGetGlyphRuns(line);
            for (int j = 0; j < runs.count; j++) {
                CTRunRef run =  (__bridge CTRunRef)runs[j];
                NSDictionary * dic = (NSDictionary *)CTRunGetAttributes(run);
                CTRunDelegateRef delegate = (__bridge CTRunDelegateRef)[dic objectForKey:(NSString *)kCTRunDelegateAttributeName];
                if (delegate == nil) {
                    continue;
                }
                NSString * imageName = [dic objectForKey:@"imageName"];
                UIImage * image = [UIImage imageNamed:imageName];
                CGRect runBounds;
                CGFloat ascent;
                CGFloat descent;
                runBounds.size.width = CTRunGetTypographicBounds(run, CFRangeMake(0, 0), &ascent, &descent, NULL);
                runBounds.size.height = ascent + descent;
                CFIndex index = CTRunGetStringRange(run).location;
                CGFloat xOffset = CTLineGetOffsetForStringIndex(line, index, NULL);
                runBounds.origin.x = lineOrigins[i].x + xOffset;
                runBounds.origin.y = lineOrigins[i].y;
                runBounds.size =image.size;
                CGContextDrawImage(context, runBounds, image.CGImage);
            }
        }
        
        CGImageRef imageRef = CGBitmapContextCreateImage(context);
        UIImage *image;
        if (imageRef) {
            image = [UIImage imageWithCGImage:imageRef];
            CGImageRelease(imageRef);
        }
        
        UIGraphicsEndImageContext();
       //在主線程中更新
        dispatch_async(dispatch_get_main_queue(), ^{
            self.layer.contents = (__bridge id _Nullable)(image.CGImage);
        });
    });
}

NSAttributedString

看yykit的demo里嫩与,微博的頁面的富文本使用了NSAttributedString寝姿,這里記錄下學(xué)習(xí)筆記。
這里要把"我家這個好忠犬啊~[喵喵] http://t.cn/Ry4UXdF //@我是呆毛芳子蜀黍w:這是什么鬼划滋?[喵喵] //@清新可口喵醬圓臉星人是扭蛋狂魔:窩家這個超委婉的拒絕了窩"在手機(jī)上顯示成;

![Uploading 屏幕快照 2017-08-17 上午11.11.20_514440.png . . .]

@用戶名用到了正則匹配饵筑,可以得到一個nsrange的數(shù)組,是@用戶名的nsrange处坪;
表情也是用到了正則匹配根资,得到每個表情的nsrange,從本地尋找表情對應(yīng)的圖片同窘,然后用到了NSTextAttachment來生成NSAttributedString玄帕,然后把表情進(jìn)行了替換。
http://t.cn/Ry4UXdF這個鏈接被替換成了圖片和文字想邦,圖片是從網(wǎng)絡(luò)上下載的裤纹。可以先判斷本地是否有圖片的緩存丧没,如果沒有鹰椒,先用占位圖生成NSTextAttachment锡移,先顯示占位圖,等圖片下載完以后就重新替換掉圖片吹零。
demo

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末罩抗,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子灿椅,更是在濱河造成了極大的恐慌套蒂,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件茫蛹,死亡現(xiàn)場離奇詭異操刀,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)婴洼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進(jìn)店門骨坑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人柬采,你說我怎么就攤上這事欢唾。” “怎么了粉捻?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵礁遣,是天一觀的道長。 經(jīng)常有香客問我肩刃,道長祟霍,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任盈包,我火速辦了婚禮沸呐,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘呢燥。我一直安慰自己崭添,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布叛氨。 她就那樣靜靜地躺著滥朱,像睡著了一般。 火紅的嫁衣襯著肌膚如雪力试。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天排嫌,我揣著相機(jī)與錄音畸裳,去河邊找鬼。 笑死淳地,一個胖子當(dāng)著我的面吹牛怖糊,可吹牛的內(nèi)容都是我干的帅容。 我是一名探鬼主播,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼伍伤,長吁一口氣:“原來是場噩夢啊……” “哼并徘!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起扰魂,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤麦乞,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后劝评,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體姐直,經(jīng)...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年蒋畜,在試婚紗的時候發(fā)現(xiàn)自己被綠了声畏。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,779評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡姻成,死狀恐怖插龄,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情科展,我是刑警寧澤均牢,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站辛润,受9級特大地震影響膨处,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜砂竖,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一真椿、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧乎澄,春花似錦突硝、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至浙于,卻和暖如春护盈,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背羞酗。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工腐宋, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓胸竞,卻偏偏與公主長得像欺嗤,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子卫枝,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,700評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 1.iOS中的round煎饼、ceil、floor函數(shù)略解 round如果參數(shù)是小數(shù)校赤,則求本身的四舍五入.ceil如果...
    K_Gopher閱讀 1,184評論 1 0
  • 系列文章: CoreText實(shí)現(xiàn)圖文混排 CoreText實(shí)現(xiàn)圖文混排之點(diǎn)擊事件 CoreText實(shí)現(xiàn)圖文混排之文...
    老司機(jī)Wicky閱讀 40,154評論 221 432
  • CoreText是一個進(jìn)階的比較底層的布局文本和處理字體的技術(shù)吆玖,CoreText API在OS X v10.5 和...
    smalldu閱讀 13,443評論 18 129
  • 很久以前寫的文章搬到這里來放著。iOS開發(fā)中經(jīng)常會遇到做一些文字排版的需求痒谴,文字圖片混排的需求衰伯,在iOS7 以前一...
    AlienJunX閱讀 606評論 0 2
  • 本文所涉及的代碼你可以在這里下載到https://github.com/kejinlu/CTTest,包含兩個項目...
    eb99d15a673d閱讀 1,251評論 0 6