CoreText入門

整理中...

文本布局

TextLayout = Glyphs + Locations
參考:活字印刷

Paste_Image.png

Glyphs(字型)?

  • 字符的圖形形式(A Graphical Representation of Characters)。
  • 字符 + 字體 -> 字型 (Character + font -> glyph)。
  • 圖形系統(tǒng)中字型編碼:CGGlyph(Glyph IDs for the graphics systems: CGGlyph)。
    Paste_Image.png

示例

+ (void)ctu_drawAttributedText:(NSAttributedString *)attributedText
                     textRange:(NSRange)textRange
                        inRect:(CGRect)rect
                      context:(CGContextRef)context
{
    NSAssert(context != NULL, @"context is NULL !");
    if (context == NULL ||
        attributedText == nil ||
        attributedText.length == 0 ||
        CGRectIsEmpty(rect) ||
        textRange.length == 0) return;
    
    /* Push a copy of the current graphics state onto the graphics state stack.*/
    CGContextSaveGState(context);
    {
        CGContextSetTextMatrix(context, CGAffineTransformIdentity);
        CGContextTranslateCTM(context, CGRectGetMinX(rect), CGRectGetMaxY(rect));
        CGContextScaleCTM(context, 1.0f, -1.0f);
        
        CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)attributedText);
        
        CGRect _rect = CGRectMake(0,0,
                                  CGRectGetWidth(rect),
                                  CGRectGetHeight(rect));
        
        CGPathRef path = CGPathCreateWithRect(_rect,
                                              nil);
        
        CFRange _textRange = CFRangeMake(textRange.location, textRange.length);
        
        CTFrameRef frame = CTFramesetterCreateFrame(framesetter,
                                                    _textRange,
                                                    path,
                                                    nil);
        CTFrameDraw(frame, context);
        
        CFRelease(frame);
        CFRelease(path);
        CFRelease(framesetter);
    }
    /* Restore the current graphics state from the one on the top of the
     graphics state stack, popping the graphics state stack in the process. */
    CGContextRestoreGState(context);
    
}
Simulator Screen Shot 2016年1月28日 11.09.55.png

CoreText

當前版本0.1
點擊此處-顯示原文

Introduction

CoreText是的iOS3.2+和OSX10.5+中的文本引擎句各,讓您精細的控制文本布局和格式按傅。它位于在UIKit中和CoreGraphics/Quartz之間的最佳點甲脏。* UIKit中你有的文本空間,你可以通過XIB簡單的使用文本控件在屏幕上顯示文字信不,但你不能改變個別字的顏色苔埋。*

CoreGraphics/Quartz你可以做幾乎可以勝任所有的工作懦砂,但是你需要計算每個字形的在文本中的位置,并繪制在屏幕上。*

CoreText正好位于兩者之間孕惜!你可以完全控制位置,布局晨炕,屬性衫画,如顏色和大小,但CoreText布局需要你自己管理-從自動換行到字體渲染等等瓮栗。

如果你正在創(chuàng)建一個iPad上的雜志或書籍的應(yīng)用程序削罩,使用CoreText非常方便。這個CoreText教程將帶你如何使用CoreText創(chuàng)建一個雜志應(yīng)用你將學(xué)習(xí)如何:* 奠定格式化的上下文本在屏幕上* 微調(diào)文本的外觀* 向文本內(nèi)容中添加圖片* 最后創(chuàng)建一個雜志的應(yīng)用程序费奸,它加載文本標記來輕松地控制渲染文本的格式* 最后吃掉你的腦子

建立一個核心文本項目為了充分利用這個CoreText教程弥激,您首先要知道iOS開發(fā)的基礎(chǔ)知識。如果你是iOS開發(fā)新手愿阐,首先你應(yīng)該看看的一些基礎(chǔ)教程.事不宜遲微服,讓我們通過自己開發(fā)一個簡單的《雜志》應(yīng)用程序:

  1. 創(chuàng)建應(yīng)用的時候選Single View Application* 添加CoreText.framework
  1. 添加一個CoreText View在UIViewdrawRect:方法中使用CoreText
  2. 創(chuàng)建一個JY_CTView繼承自UIView缨历。* 其次以蕴,在Storyboard中添加一個UIView,就像這樣:
    github
    github
  3. 最后在 drawRect函數(shù)中繪制文本蒼老師
-(void)drawRect:(CGRect)rect{ 
// Drawing code
 CGContextRef ref = UIGraphicsGetCurrentContext();
CGMutablePathRef path = CGPathCreateMutable();
//1
 CGPathAddRect(path, NULL, self.bounds); 
NSAttributedString* attString = [[NSAttributedString alloc] initWithString:@"蒼老師辛孵!"];
//2
   CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attString);
//3
 CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, attString.length), path, NULL); 
CTFrameDraw(frame, ref); 
//4 
 CFRelease(framesetter); 
//5
 CFRelease(path); CFRelease(frame);}

好吧讓我們來討論這個丛肮,使用上面的注釋標記來指定每個部分:

  1. 在這里,你需要創(chuàng)建一個邊界魄缚,在區(qū)域的路徑中您將繪制文本宝与。(就是說我給你指定一個帳號,你必需給指定帳號匯錢)冶匹。在MaciOSCoreText支持不同的形狀习劫,如矩形和圓。在這個簡單的例子中徙硅,您將使用整個視圖范圍為在那里您將通過創(chuàng)建從self.bounds一個CGPath參考繪制矩形榜聂。

  2. 在核心文字你不使用的NSString,而是NSAttributedString嗓蘑,如下圖所示须肆。NSAttributedString是一個非常強大的NSString衍生類,它允許你申請的格式屬性的文本桩皿。就目前而言豌汇,我們不會使用格式 - 這里只是創(chuàng)建了一個純文本字符串。

  3. CTFramesetter當采用CoreText繪制文本最重要的一個類泄隔,它管理你的字體引用和你的文本繪制框架拒贱。就目前而言,你需要知道的是,CTFramesetterCreateWithAttributedString為您創(chuàng)建一個CTFramesetter逻澳,保留它闸天,并用附帶的屬性字符串初始化它。在這部分中斜做,之后使用CTFramesetterCreateFrame得到frameframesetterpath苞氮,(我們選擇整個字符串在這里),并在繪制時瓤逼,文字會出現(xiàn)在矩形

  4. CTFrameDraw在提供的大小在給定上下文后繪制笼吟,蒼老師5. 最后,所有使用的對象被釋放請注意霸旗,您使用一套像CTFramesetterCreateWithAttributedStringCTFramesetterCreateFrame功能贷帮,而不是直接使用Objective-C對象CoreText類時。你可能會認為自己“為什么我會要再次使用C诱告,我認為我應(yīng)該用Objective-C去完成撵枢?”好了,很多iOS上的底層庫中都在使用標準C蔬啡,因為速度和簡單诲侮。不過別擔心,你會發(fā)現(xiàn)CoreText函數(shù)很容易箱蟆。只是一個要記住最重要的一點:不要忘記使用CFRelease釋放內(nèi)存沟绪。不管你信不信,這就是你使用CoreText繪制一些簡單的文本

  5. 點擊運行:


    github
    github

嗯空猜!這不是我的蒼老師绽慈?因為像許多低級別的API,CoreText采用了Y坐標系翻轉(zhuǎn)辈毯。因為這個使事情變得更糟坝疼,內(nèi)容也呈現(xiàn)向下翻轉(zhuǎn)!(CoreText因為是用了笛卡爾坐標系)谆沃,請記住钝凶,如果你混合UIKit的繪畫和CoreText繪畫,你可能會得到奇怪的結(jié)果讓我們來解決的內(nèi)容方向唁影!
修改代碼

-(void)drawRect:(CGRect)rect{
 // Drawing code 
CGContextRef ref = UIGraphicsGetCurrentContext(); 
//flip the coordinate system 
CGContextSetTextMatrix(conRef, CGAffineTransformIdentity); 
CGContextTranslateCTM(conRef, 0, self.bounds.size.height); 
CGContextScaleCTM(conRef, 1.0, -1.0);  CGMutablePathRef 
path = CGPathCreateMutable();
//1
CGPathAddRect(path, NULL, self.bounds);
NSAttributedString* attString = [[NSAttributedString alloc] initWithString:@"蒼老師耕陷!"];
//2
 CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attString);
//3
CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, attString.length), path, NULL); 
CTFrameDraw(frame, ref);
//4  
CFRelease(framesetter); 
//5
 CFRelease(path);
 CFRelease(frame);}

這是非常簡單的代碼,剛剛翻轉(zhuǎn)的內(nèi)容通過應(yīng)用轉(zhuǎn)換到視圖的上下文据沈。每一次繪制文本的時候只需要復(fù)制/粘貼它(就是把這一行代碼在繪制文本前哟沫,從copy過去就行了)。
再次運行一下锌介,看蒼老師是不是又回來了嗜诀。

github
github

The Core Text Object Model(Core Text對象模型)

如果你是一個有點困惑CTFramesetterCTFrame沒關(guān)系猾警。在這里,我會做一個簡短解釋CoreText是如何呈現(xiàn)的文字內(nèi)容隆敢。下面看起來像是CoreText對象模型:

github
github

您可以用NSAttributedString創(chuàng)建一個CTFramesetterRef发皿,同時CTTypesetter的實例將自動為您創(chuàng)建,管理您的字體類拂蝎。

接下來您使用CTFramesetter創(chuàng)建一個或多個CTFrame您在其中會呈現(xiàn)文本雳窟。

當你創(chuàng)建一個CTFrame您要它文字將其矩形的范圍內(nèi)呈現(xiàn),然后CoreText自動為文本的每一行文字匣屡,

創(chuàng)建一個CTLine(注意一個CTRun,每個CTRun塊具有相同的格式 )例子,核心文本將創(chuàng)建一個CTRun如果你有幾個單詞在一排紅色拇涤,接著又CTRun加粗句子捣作。再等等,非常重要的 - 你沒有創(chuàng)建CTRun實例鹅士,CoreText創(chuàng)建它根據(jù)你提供的NSAttributedString中的屬性每個CTRun的對象可以采取不同的屬性券躁,所以你必須很好地控制字距、連字掉盅,寬度也拜,高度等。

Onto the Magazine App!(雜志應(yīng)用程序)

要創(chuàng)建這個雜志的應(yīng)用程序, 我們需要標記一些文本具有不同的屬性的能力趾痘。我們可以做到這一點通過直接使用在NSAttributedString中的方法如setAttributes:range慢哈,但是在實踐中這是笨拙的處理方式(除非你喜歡刻意寫一噸的代碼!)所以為了讓事情更簡單與合作永票,我們將創(chuàng)建一個簡單的文本標記解析器卵贱,這將使我們能夠使用簡單的標簽來在雜志內(nèi)容設(shè)置格式。

  1. 選擇File>New>New File侣集。
  2. 選擇iOS>Cocoa Touch>Objective-C class*键俱。
  3. 然后單擊下一步。輸入NSObject為父類世分。
  4. 單擊下一步编振,命名新類MarkupParser。
  5. 然后單擊保存臭埋。
//MarkupParser.h
#import <Foundation/Foundation.h>
#import <CoreText/CoreText.h>
@interface JYMarkParser : NSObject
@property(strong,nonatomic) NSString* font;
@property(strong,nonatomic) UIColor* color;
@property(strong,nonatomic) UIColor* strokeColor;
@property(assign,readwrite) float strokeWidth;
@property(strong,nonatomic) NSMutableArray* images;
-(NSAttributedString*)attrStringFromMark:(NSString*)html;
@end
//MarkupParser.m
#import "JYMarkParser.h"
@implementation JYMarkParser
-(id)init
{ 
  self = [super init]; 
  if (self)
  {
    self.font = @"Arial"; 
    self.color = [UIColor blackColor]; 
    self.strokeColor = [UIColor whiteColor];
    self.strokeWidth = 0.0;
    self.images = [NSMutableArray array]; 
  } 
  return self;
}
-(NSAttributedString*)attrStringFromMark:(NSString*)markup
{ 
 return nil;
}
-(void)dealloc
{ 
  self.font = nil; 
  self.color = nil; 
  self.strokeColor = nil;
  self.images = nil;
}
@end

正如你看到你開始解析器代碼很簡單踪央,它只是包含屬性來保存字體,文本顏色斋泄,筆畫寬度和筆畫顏色杯瞻。

稍后我們將添加里面的文字圖像,所以你需要保持在文字圖像列表的數(shù)組炫掐。編寫解析器通常是很艱苦的工作魁莉,所以我要告訴你如何建立一個非常非常簡單的使用正則表達式。本教程的解析器將非常簡單,只支持打開標簽 - 即標記將設(shè)置標記后的文本的樣式旗唁,樣式將應(yīng)用到一個新的標簽被發(fā)現(xiàn)畦浓。該文本標記看起來像這樣:
github
github

并產(chǎn)生這樣的輸出:
github
github

對于本教程的目的,這樣的標記將是相當足夠了检疫。為你的項目可以進一步開發(fā)它讶请,如果你想更牛B的功能的話。

Let’s go在attrStringFromMark:方法中添加以下內(nèi)容:

-(NSAttributedString*)attrStringFromMark:(NSString*)markup
{
    NSMutableAttributedString* aString = [[NSMutableAttributedString alloc] initWithString:@""];
    //1
    NSError* error = nil;
    //(.*?).通配符 *屎媳?匹配上一個元素零次或多次夺溢,但次數(shù)盡可能少。
    //^匹配必須從字符串或一行的開頭開始烛谊。
    //<>的位置
    NSRegularExpressionOptions options = NSRegularExpressionCaseInsensitive|NSRegularExpressionDotMatchesLineSeparators;
    NSRegularExpression* regex = [[NSRegularExpression alloc]initWithPattern:@"(.*?)(<[^>]+>|\\Z)"
                                                                     options:options
                                                                       error:&error];
    //2
    NSArray* chunks = [regex matchesInString:markup
                                     options:0
                                       range:NSMakeRange(0, markup.length)];
    if (error)
    {
        NSLog(@"解析標簽出現(xiàn)錯誤:%@\n%@",[error userInfo],error);
        //返回原來的字符串
        return [[NSAttributedString alloc] initWithString:markup];
    }
}

有兩個章節(jié)风响,這里包括:

  1. 首先,初始化一個空的NSMutableAttributedString

  2. 接下來丹禀,你需要創(chuàng)建一個正則表達式來匹配文本和標簽快状勤。這個正則表達式將匹配基本文本字符串和下列標記,正則表達式選找匹配的字符串双泪,直到你遇到<然后匹配任何數(shù)量的字符持搜,直到你遇到>或者\n。為什么要創(chuàng)建這個正則表達式焙矛?我們將用它來搜索字符串的每個匹配的地方葫盼,然后

a. 找到要修改樣式的字符串,然后
b. 根據(jù)解析出來的樣式村斟,改變字符串的顏色剪返,字體等。重復(fù)1邓梅、2的步驟改變每一處樣式脱盲。很簡單的解析器,不是嗎日缨?現(xiàn)在數(shù)組chunks中你擁有了所有的標記和需要修改的文本钱反,你需要循chunks從其中取得要字符串和樣式

-(NSAttributedString*)attrStringFromMark:(NSString*)mark
{
    NSMutableAttributedString* aString = [[NSMutableAttributedString alloc] initWithString:@""];
    NSError* error = nil;
    //(.*?).通配符 *?匹配上一個元素零次或多次匣距,但次數(shù)盡可能少面哥。
    //^匹配必須從字符串或一行的開頭開始。
    //<>的位置
    NSRegularExpressionOptions options = NSRegularExpressionCaseInsensitive|NSRegularExpressionDotMatchesLineSeparators;
    NSRegularExpression* regex = [[NSRegularExpression alloc]initWithPattern:@"(.*?)(<[^>]+>|\\Z)"
                                                                     options:options
                                                                       error:&error];
    NSArray* chunks = [regex matchesInString:mark
                                     options:0
                                       range:NSMakeRange(0,
                                                         mark.length)];
    //1
    if (error) { NSLog(@"解析標簽出現(xiàn)錯誤:%@\n%@",[error userInfo],error);
        //返回原來的字符串
        return [[NSAttributedString alloc] initWithString:mark];
    }
    // NSLog(@"%@",chunks);
    for (NSTextCheckingResult* result in chunks)
    {
        //字符串切割
        NSArray* parts = [[mark substringWithRange:result.range] componentsSeparatedByString:@"<"];
        //1;
        CTFontRef fontRef = CTFontCreateWithName((__bridge CFStringRef)self.font, 24.0f, NULL);
        //apply the current text style
        //2
        NSDictionary* attrs = @{(id)kCTForegroundColorAttributeName: (id)self.color.CGColor,
                                (id)kCTFontAttributeName:(__bridge id)fontRef,
                                (id)kCTStrokeColorAttributeName:(__bridge id)self.strokeColor.CGColor,
                                (id)kCTStrokeWidthAttributeName:[NSNumber numberWithFloat:self.strokeWidth]};
        [aString appendAttributedString:[[NSAttributedString alloc] initWithString:parts[0]
                                                                        attributes:attrs]];
        CFRelease(fontRef);
        //是否帶屬性毅待,處理新的樣式 3
        if (parts.count>1)
        {
            NSString* tag = parts[1];
            if ([tag hasPrefix:@"font"])
            {
                //stroke color
                NSRegularExpression* scReg = [[NSRegularExpression alloc]initWithPattern:@"(?<=strokeColor=\")\\w+"
                                                                                 options:0
                                                                                   error:nil];
                [scReg enumerateMatchesInString:tag
                                        options:0
                                          range:NSMakeRange(0, tag.length)
                                     usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop)
                {
                    if ([[tag substringWithRange:result.range] isEqualToString:@"none"])
                    {
                        self.strokeWidth = 0.0;
                    }
                    else
                    {
                        self.strokeWidth = -3.0;
                        SEL colorSel = NSSelectorFromString([NSString stringWithFormat:@"%@Color",[tag substringWithRange:result.range]]);
                        self.strokeColor = [UIColor performSelector:colorSel];
                    }
                }];
                //Color
                NSRegularExpression* colorReg = [[NSRegularExpression alloc] initWithPattern:@"(?<=color=\")\\w+"
                                                                                     options:0
                                                                                       error:nil];
                [colorReg enumerateMatchesInString:tag
                                           options:0
                                             range:NSMakeRange(0, tag.length)
                                        usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop)
                 {
                     SEL colorSel = NSSelectorFromString([NSString stringWithFormat: @"%@Color", [tag substringWithRange:result.range]]);
                     self.color = [UIColor performSelector:colorSel];
                 }];
                //face
                NSRegularExpression* faceRegex = [[NSRegularExpression alloc] initWithPattern:@"(?<=face=\")[^\"]+"
                                                                                      options:0
                                                                                        error:NULL];
                [faceRegex enumerateMatchesInString:tag
                                            options:0
                                              range:NSMakeRange(0,
                                                                [tag length])
                                         usingBlock:^(NSTextCheckingResult *match, NSMatchingFlags flags, BOOL *stop)
                {
                    self.font = [tag substringWithRange:match.range];
                }];
            }
        }
    }
    //end of font parsing 結(jié)束字體解析
    return aString;
}

尼瑪尚卫,這是一個很大的代碼!但不用擔心尸红,我們在這里逐節(jié)介紹:

  1. 快速枚舉chunks數(shù)組中我們用正則找到的NSTextCheckingResult對象吱涉,對“chunks”數(shù)組中的元素用<字符分割(<是標簽的起始)刹泄。其結(jié)果,在parts [0]中的內(nèi)容添加到aString中(aString是一個NSAttributedString)怎爵,接下來在parts[1]中你有標記的內(nèi)容為后面的文本改變格式特石。
  2. 其次,你創(chuàng)建一個字典保持一系列的格式化選項- 這是你可以通過格式屬性的NSAttributedString的方式”盍矗看看這些Key的名稱- 他們是蘋果定義的常量(詳情請圍觀參考)姆蘸。通過調(diào)用appendAttributedString: 新的文本塊與應(yīng)用格式被添加到結(jié)果字符串。
  3. 最后芙委,你檢查如果有文字后發(fā)現(xiàn)了一個標記逞敷;如果以font開頭的正則表達式每一種可能的標記屬性。對于face屬性的字體的名稱保存在self.font灌侣,為color我和你做了一點改變:對<font color="red">文本值red采取的是colorRegex兰粉,然后選擇器redColor被創(chuàng)建和執(zhí)行在UIColor類 - 這(嘿嘿)返回一個紅色的的UIColor實例(在實際中可以使用#FFFFFFFF這種方式裝換成顏色,網(wǎng)上有自己找找)顶瞳,請注意這個技巧只適用于的UIColor的預(yù)定義的顏色(如果你調(diào)用了一個UIColor中不存在的方法,你的代碼會奔潰c碉)但是這足以滿足本教程慨菱。stroke color屬性的工作原理很像顏色屬性,但如果則strokeColor的值為none剛剛設(shè)置筆觸widht0.0戴甩,所以stroke沒有將被應(yīng)用到的文本符喝。

Note:如果你好奇在本節(jié)中正則表達式是如何工作,請閱讀NSRegularExpression class reference甜孤。

沒錯协饲!繪制格式化文本的一半工作完成- 現(xiàn)在用attrStringFromMark:可以得到一個有標記的NSAttributedString輸出到CoreText

因此缴川,讓我們傳遞一個字符串來呈現(xiàn)茉稠,并嘗試一下!打開JY_CTView.m,修改drawRect:

-(void)drawRect:(CGRect)rect
{
    // Drawing code
    CGContextRef ref = UIGraphicsGetCurrentContext();
    //flip the coordinate system
    CGContextSetTextMatrix(conRef, CGAffineTransformIdentity);
    CGContextTranslateCTM(conRef, 0, self.bounds.size.height);
    CGContextScaleCTM(conRef, 1.0, -1.0);
    CGMutablePathRef path = CGPathCreateMutable();
    //1
    CGPathAddRect(path, NULL, self.bounds);
    //Parser tag
    JYMarkParser* p = [[JYMarkParser alloc]init];
    NSAttributedString * attString = [p attrStringFromMark: @"Hello <font color=\"red\">core text <font color=\"blue\">world!"];
    CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attString);
    //3 CTFrameRef frame =
    CTFramesetterCreateFrame(framesetter, CFRangeMake(0, attString.length), path, NULL); CTFrameDraw(frame, ref);
    //4
    CFRelease(framesetter);
    //5
    CFRelease(path);
    CFRelease(frame);
}

你上面做一個新的解析器把夸,給它一塊標記而线,它給你返回格式化的文本。點擊運行和自己試試看恋日!

github
github
是不是真棒膀篮?由于50行的解析,我們不必處理文本范圍和代碼重文本格式岂膳,我們現(xiàn)在可以只使用一個簡單的文本在Magazine app中誓竿。此外剛剛編寫的簡單的解析器,可以無限擴展谈截,支持一切你需要的功能筷屡。

A Basic Magazine Layout(一個基礎(chǔ)的雜志布局)

到目前為止涧偷,我們的文字顯示出來,它是一個很好的第一步速蕊。但對于一本雜志嫂丙,我們希望有列 - 而這正是CoreText變得特別方便。在繼續(xù)進行布局代碼规哲,讓我們先加載一個更長的字符串到應(yīng)用程序跟啤,所以我們有一些足夠長的多行換行。把這個點擊下載test.txt拷貝到項目中唉锌。
然后在JYController中添加一下代碼

#import "JYViewController.h"#import "JY_CTView.h"#import "JYMarkParser.h"@interface JYViewController()@property (weak, nonatomic) IBOutlet JY_CTView *contentView;@end@implementation JYViewController- (void)viewDidLoad{ [super viewDidLoad]; NSString* string = [NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"test" ofType:@"txt"] encoding:NSUTF8StringEncoding error:nil]; JYMarkParser* mp = [[JYMarkParser alloc]init]; [_contentView setAttString:[mp attrStringFromMark:string]];}@end```當應(yīng)用程序的視圖被加載隅肥,應(yīng)用程序從`test.txt`的讀取文本,將其轉(zhuǎn)換為一個屬性字符串袄简,然后設(shè)置在窗口的視圖`attString`屬性腥放。我們還沒有添加該屬性到`JY_CTView`,所以讓我們添加了下绿语!在`JY_CTView.h`定義這3個實例變量:```float frameXOffset```秃症、```float frameYOffset; ```、```NSAttributedString* attString;```然后加入相應(yīng)的代碼`JY_CTView.h`來定義`attString`一個屬性:```#import "JY_CTColumnView.h"@interface JY_CTView : UIView{ CGFloat _frameXOffset; CGFloat _frameYOffset;}@property(nonatomic,copy) NSAttributedString* attString;```在運行之前先刪除```JY_CTView.m```的```drawRect:```方法中相關(guān)代碼```//Parser tagJYMarkParser* p = [[JYMarkParser alloc]init];NSAttributedString * attString = [p attrStringFromMark: @"Hello <font color=\"red\">core text <font color=\"blue\">world!"];```現(xiàn)在吕粹,您可以再次點擊運行來查看該文本文件的內(nèi)容的視圖种柑。酷匹耕!... ![github](https://raw.githubusercontent.com/AchillesWang/CoreText/master/Magazine/image/img05.jpg "github") 這個文本如何分列聚请?幸運的是核心文本提供了一個方便的功能 -`CTFrameGetVisibleStringRange`。這個函數(shù)告訴你多少文字會放入一個給定的`frame`稳其。這樣的想法是 - 創(chuàng)建列驶赏,檢查多少文字適合在里面,如果有更多的 - 創(chuàng)建另一列既鞠,首先 - 我們將會有列煤傍,那么頁面,然后一整本雜志嘱蛋,所以......讓我們使我們的`JY_CTView`子類`UIScrollView`中得到自由分頁和滾動患久!打開CTView.h和更改繼承關(guān)系`@interface JY_CTView : UIScrollView<UIScrollViewDelegate>`OK!我們已經(jīng)得到了自由滾動和翻頁現(xiàn)已推出浑槽。我們要啟用分頁在一分鐘內(nèi)蒋失。截至目前,我們正在創(chuàng)建我們的`CTFramesetter`和`CTFrame`在`drawRect:`方法內(nèi)桐玻。當你有列和不同的格式最好是做所有這些計算一次篙挽。所以,我們要做的是擁有新的類`JY_CTColumnView`.因此镊靴,要總結(jié):`JY_CTView`是要采取搭理滾動铣卡,分頁和建設(shè)列链韭,`JY_CTColumnView`實際上會呈現(xiàn)在屏幕上的內(nèi)容。這個類確實差不多就是這樣- 它只是呈現(xiàn)CTFrame煮落。我們將在該雜志的每個文本列上創(chuàng)建它的一個實例敞峭。讓我們首先添加一個屬性來保存我們的`JY_CTView`的`frames`并聲明`buildFrames`方法,它會做的列設(shè)置:```@property(nonatomic,strong) NSMutableArray* frames;-(void)buildFrames;```現(xiàn)在`buildFrames`可以創(chuàng)建文本框蝉仇,并將其存儲在“frames”數(shù)組旋讹。讓我們添加這樣做的代碼。```-(void)buildFrames{ _frameXOffset = 20; //1 _frameYOffset = 20; self.pagingEnabled = YES; self.delegate = self; self.frames = [NSMutableArray array]; CGMutablePathRef path = CGPathCreateMutable();//2 CGRect textFrame = CGRectInset(self.bounds, _frameXOffset, _frameYOffset); CGPathAddRect(path, NULL, textFrame);  CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)_attString);  int textPos = 0;//3 int columnIndex = 0; while (textPos<self.attString.length) {//4 CGPoint colOffset = CGPointMake((columnIndex+1)*_frameXOffset+columnIndex*(textFrame.size.width/2),20); CGRect colRect = CGRectMake(0, 0, textFrame.size.width/2-10, textFrame.size.height-40);  CGMutablePathRef path = CGPathCreateMutable(); CGPathAddRect(path, NULL, colRect);  //use the column path CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(textPos, 0), path, NULL); CFRange frameRange = CTFrameGetVisibleStringRange(frame);//5  //create an empty column view JY_CTColumnView* content = [[JY_CTColumnView alloc]initWithFrame:CGRectMake(0, 0, self.contentSize.width, self.contentSize.height)]; content.backgroundColor = [UIColor clearColor]; content.frame = CGRectMake(colOffset.x, colOffset.y, colRect.size.width, colRect.size.height);  //set the column view contents and add it as subview [content setCTFrame:(__bridge id)frame]; //6 [self.frames addObject:(__bridge id)frame]; [self addSubview:content];  //prepare for next frame textPos += frameRange.length; //CFRelease(frame); CFRelease(path); columnIndex++; }  //set the total width of the scroll view int totalPages = (columnIndex+1)/2;//7 self.contentSize = CGSizeMake(totalPages*self.bounds.size.width, textFrame.size.height); }

讓我們來看看代碼

  1. 這里我們做一些設(shè)置 - 定義X和Y偏移轿衔,啟用分頁并創(chuàng)建一個空的frames數(shù)組
  2. buildFrames繼續(xù)通過創(chuàng)建一個路徑和視圖的邊界frame(稍有偏差沉迹,所以我們有邊距)
  3. 該段說明textPos,這將保持當前位置的文本害驹。這也聲明columnIndex鞭呕,這將計算已經(jīng)創(chuàng)建多少列。
  4. 這里的while循環(huán)運行宛官,直到我們到達了文本的末尾葫松。在循環(huán)中,我們創(chuàng)建一個列范圍:colRect是的CGRect底洗,要看columnIndex保持當前列的原點和大小腋么。請注意,我們正在不斷建立列在右邊(不能跨枷恕,然后向下)。
  5. 這使得利用CTFrameGetVisibleStringRange功能要弄清楚什么部分的字符串可以容納在CGRect(在這種情況下谭胚,文本列)徐块。 textPos是這個范圍的長度增加,所以下一列的建設(shè)可以在下一循環(huán)開始(如果有多個文本剩余)灾而。
  6. 而不是像繪畫前frame在這里胡控,我們把它傳遞給新創(chuàng)建的JY_CTColumnView,我們將其存儲在self.frames數(shù)組為以后的使用旁趟,我們把它作為子視圖(ScrollView中)昼激。
  7. 最后,totalPages持有所產(chǎn)生的總頁數(shù)锡搜,以及JY_CTViewcontentSize屬性設(shè)置橙困,所以當有內(nèi)容多于一頁,我們得到滾動是自由的「停現(xiàn)在凡傅,讓我們也調(diào)用buildFrames當所有的CoreText設(shè)置完成了。里面JYViewController.m添加在viewDidLoad中的結(jié)尾:[_contextView buildFrames]還有一件事讓新代碼嘗試前做肠缔,在文件JY_CTView.m找到方法的drawRect:將其刪除夏跷。我們現(xiàn)在做的所有繪制在JY_CTColumnView類中哼转,所以我們不需要drawRect:方法,專注實現(xiàn)ScrollView的功能槽华。好吧……點擊運行壹蔓,你會看到成列的文本,還可以進行拖動猫态。 我們有列格式化文本佣蓉,但我們錯過的圖片。原來繪制的圖像與文字的核心是不那么容易 - 這畢竟是一個文本框架懂鸵。但由于這樣的事實偏螺,我們已經(jīng)有了一個小標記解析器我們要拿到里面的文字圖像!

Drawing Images in Core Text(CoreText中繪制圖像)

基本上核心文本不具有繪制圖像的可能性匆光。然而套像,因為它是一個布局引擎,可以做的是給要畫一幅畫留下一個空的空間终息。而且夺巩,由于你的代碼已經(jīng)在drawRect:方法里面。你自己繪制一張圖片很容易周崭。讓我們來看看如何在文本留下空白用于繪制圖像柳譬,記住所有的文字塊是CTRun實例!你只需設(shè)置一個委托為給定的CTRun并且委托對象負責(zé)要讓CoreText知道CTRun上升空間下降空間寬度续镇。像這樣:

github
github

CoreText到達一CTRun其中有一個CTRunDelegate它會詢問委托- 多么寬美澳,我應(yīng)該留給這個塊的數(shù)據(jù),有多高摸航,應(yīng)該是什么制跟?這樣,在你建立的文本空白處 - 然后你畫你的圖片在那個非常的地方。讓我們先添加一個IMG標簽支持在我們的小標記解析器酱虎!打開JYMarkupParser并且找到} //end of font parsing;

在這一行后面雨膨,立即添加下面的代碼添加為IMG標簽的支持:

if ([tag hasPrefix:@"img"]) {  __block NSNumber* width = [NSNumber numberWithInt:0]; __block NSNumber* height = [NSNumber numberWithInt:0]; __block NSString* fileName = @"";  //width NSRegularExpression* widthRegex = [[NSRegularExpression alloc] initWithPattern:@"(?<=width=\")[^\"]+" options:0 error:NULL] ; [widthRegex enumerateMatchesInString:tag options:0 range:NSMakeRange(0, [tag length]) usingBlock:^(NSTextCheckingResult *match, NSMatchingFlags flags, BOOL *stop){ width = [NSNumber numberWithInt: [[tag substringWithRange: match.range] intValue] ]; }];  //height NSRegularExpression* faceRegex = [[NSRegularExpression alloc] initWithPattern:@"(?<=height=\")[^\"]+" options:0 error:NULL] ; [faceRegex enumerateMatchesInString:tag options:0 range:NSMakeRange(0, [tag length]) usingBlock:^(NSTextCheckingResult *match, NSMatchingFlags flags, BOOL *stop){ height = [NSNumber numberWithInt: [[tag substringWithRange:match.range] intValue]]; }];  //image NSRegularExpression* srcRegex = [[NSRegularExpression alloc] initWithPattern:@"(?<=src=\")[^\"]+" options:0 error:NULL]; [srcRegex enumerateMatchesInString:tag options:0 range:NSMakeRange(0, [tag length]) usingBlock:^(NSTextCheckingResult *match, NSMatchingFlags flags, BOOL *stop){ fileName = [tag substringWithRange: match.range]; }];  //add the image for drawing [self.images addObject: [NSDictionary dictionaryWithObjectsAndKeys: width, @"width", height, @"height", fileName, @"fileName", [NSNumber numberWithInt: [aString length]], @"location", nil] ]; NSLog(@"%@", [NSDictionary dictionaryWithObjectsAndKeys: width, @"width", height, @"height", fileName, @"fileName", [NSNumber numberWithInt: [aString length]], @"location", nil]);  //render empty space for drawing the image in the text //1 CTRunDelegateCallbacks callbacks; callbacks.version = kCTRunDelegateVersion1; callbacks.getAscent = ascentCallback; callbacks.getDescent = descentCallback; callbacks.getWidth = widthCallback; callbacks.dealloc = deallocCallback;  NSDictionary* imgAttr = [NSDictionary dictionaryWithObjectsAndKeys: //2 width, @"width", height, @"height", nil];  CTRunDelegateRef delegate = CTRunDelegateCreate(&callbacks, (__bridge void *)(imgAttr)); //3 NSDictionary *attrDictionaryDelegate = [NSDictionary dictionaryWithObjectsAndKeys: //set the delegate (__bridge id)delegate, (NSString*)kCTRunDelegateAttributeName, nil];  //add a space to the text so that it can call the delegate [aString appendAttributedString:[[NSAttributedString alloc] initWithString:@" " attributes:attrDictionaryDelegate]];}

讓我們來看看所有的新代碼 - 實際上解析IMG標簽和解析字體標簽確實幾乎是一樣的,通過使用3個正則表達式你有效地檢索img標簽的寬度读串,高度和src屬性聊记。當完成 - 你添加一個新的NSDictionary持有你剛剛解析出來的信息,再加上圖像在文本的位置恢暖,最后添加到self.images中排监。

現(xiàn)在看第1 部分- CTRunDelegateCallbacks是一個C結(jié)構(gòu)體,持有引用功能杰捂。這個結(jié)構(gòu)體提供了你想傳遞給CTRunDelegate的信息社露。正如你已經(jīng)可以猜到的getWidth被調(diào)用來提供對CTRun的寬度,getAscent提供CTRun的高度琼娘。在你上面的代碼提供該些處理程序的函數(shù)名; 稍后我們要添加的函數(shù)主體峭弟。

第2節(jié)是非常重要的 – imgAtt字典持有的圖像的尺寸; 這個對象將被retain一下在非ARC附鸽,因為它將要傳遞給函數(shù)處理-所以,當getAscent處理函數(shù)觸發(fā)時它會得到參數(shù)imgAttr字典瞒瘸,然后讀取圖片的高度坷备,并且提供值給CoreText。(就是這個feel倍爽)情臭!CTRunDelegateCreate

在第3節(jié)創(chuàng)建一個委托實例和綁定的回調(diào)與數(shù)據(jù)參數(shù)省撑。在接下來的步驟中,您需要創(chuàng)建的屬性字典(以同樣的方式作為上述字體的格式)俯在,不能直接使用CTRunDelegateRef竟秫。到最后你加一個空格去觸發(fā)delagate圖像將被渲染。

下一步跷乐,你已經(jīng)預(yù)料肥败,是提供的回調(diào)函數(shù)給委托:

/* Callbacks */static void deallocCallback( void* ref ){ ref =nil;}static CGFloat ascentCallback( void *ref ){ return [(NSString*)[(__bridge NSDictionary*)ref objectForKey:@"height"] floatValue];}static CGFloat descentCallback( void *ref ){ return [(NSString*)[(__bridge NSDictionary*)ref objectForKey:@"descent"] floatValue];}static CGFloat widthCallback( void* ref ){ return [(NSString*)[(__bridge NSDictionary*)ref objectForKey:@"width"] floatValue];}

ascentCallbackdescentCallbackwidthCallback讀各屬性從字典中并且提供給CoreText愕提。deallocCallback做的是什么馒稍?它釋放的字典保存的圖像信息- 這就是所謂的當CTRunDelegate得到釋放,讓你有機會做你的內(nèi)存管理∏城龋現(xiàn)在纽谒,您的解析器處理“IMG”標簽,讓我們也調(diào)整CTView來呈現(xiàn)它們如输。我們需要一種方法來將圖像數(shù)組發(fā)送到視圖鼓黔,讓我們結(jié)合設(shè)置屬性字符串和圖像轉(zhuǎn)聲明一個新方法。

添加的代碼:

JY_CTView.h@property(nonatomic,strong) NSArray* images;-(void)setAttString:(NSAttributedString*)attString withImages:(NSArray*)imgs;JY_CTView.m-(void)setAttString:(NSAttributedString *)attString withImages:(NSArray *)imgs{ self.attString = attString; self.images = imgs;}

現(xiàn)在不见,CTView準備接受與圖像數(shù)組澳化,讓我們來分析并且使用他們。去JYViewController.m并找到行[contentView setAttString:attString]; - 用下面的替換:[_contentView setAttString:attString withImages:mp.images];

如果您查找attrStringFromMark:在JYMarkupParser類脖祈,你會看到它保存所有的圖像標記數(shù)據(jù)到self.images肆捕。這是現(xiàn)在您所傳遞什么直接向CTView刷晋。渲染圖像盖高,我們就必須知道圖像應(yīng)該出現(xiàn)在什么地方。要找到那個地方我們需要知道若干個值的由來:* contentOffset當內(nèi)容被滾動* CTView的frame偏移(frameXOffset眼虱,frameYOffset)* CTLine原點坐標(CTLine可能在例如段落的開頭已偏移)* CTRun的原點和CTLine的原點之間的距離 讓我們來渲染這些圖片喻奥!首先,我們需要更新JY_CTColumnView類:

 //JY_CTColumnView.h@property(nonatomic,strong) NSMutableArray *images;```

//JY_CTColumnView.m- (void)drawRect:(CGRect)rect{ // Drawing code CGContextRef conRef = UIGraphicsGetCurrentContext(); //flip the coordinate system CGContextSetTextMatrix(conRef, CGAffineTransformIdentity); CGContextTranslateCTM(conRef, 0, self.bounds.size.height); CGContextScaleCTM(conRef, 1.0, -1.0); CTFrameDraw((__bridge CTFrameRef)_ctFrame, conRef); for (NSArray* imageData in self.images) { UIImage* img = [imageData objectAtIndex:0]; CGRect imgBounds = CGRectFromString([imageData objectAtIndex:1]); CGContextDrawImage(conRef, imgBounds, img.CGImage); }}

所以用這個代碼更新我們添加代碼和一個名為Images屬性捏悬,
我們將不斷出現(xiàn)在每個文本列中的圖像列表撞蚕。

為了避免聲明的又一新的類來保存保存圖像內(nèi)的圖像數(shù)據(jù),我們存儲圖像內(nèi)的圖像數(shù)據(jù)用NSArray:
1. 一個UIImage實例
2. 圖像邊界-圖像在文字中的原點和大小而現(xiàn)在过牙,計算圖像“的位置甥厦,并將它們附加到相應(yīng)的文本列的代碼:

-(void)attachImagesWithFrame:(CTFrameRef)f inColumnView:(JY_CTColumnView*)col{ //drawing images NSArray *lines = (NSArray )CTFrameGetLines(f); //1 CGPoint origins[[lines count]]; CTFrameGetLineOrigins(f, CFRangeMake(0, 0), origins); //2 int imgIndex = 0; //3 NSDictionary nextImage = [self.images objectAtIndex:imgIndex]; int imgLocation = [[nextImage objectForKey:@"location"] intValue]; //find images for the current column CFRange frameRange = CTFrameGetVisibleStringRange(f); //4 while ( imgLocation < frameRange.location ) { imgIndex++; if (imgIndex>=[self.images count]) return; //quit if no images for this column nextImage = [self.images objectAtIndex:imgIndex]; imgLocation = [[nextImage objectForKey:@"location"] intValue]; } NSUInteger lineIndex = 0; for (id lineObj in lines) { //5 CTLineRef line = (__bridge CTLineRef)lineObj; for (id runObj in (NSArray *)CTLineGetGlyphRuns(line)) { //6 CTRunRef run = (__bridge CTRunRef)runObj; CFRange runRange = CTRunGetStringRange(run); if ( runRange.location <= imgLocation && runRange.location+runRange.length > imgLocation ) { //7 CGRect runBounds; CGFloat ascent;//height above the baseline CGFloat descent;//height below the baseline runBounds.size.width = CTRunGetTypographicBounds(run, CFRangeMake(0, 0), &ascent, &descent, NULL); //8 runBounds.size.height = ascent + descent; CGFloat xOffset = CTLineGetOffsetForStringIndex(line, CTRunGetStringRange(run).location, NULL); //9 runBounds.origin.x = origins[lineIndex].x + self.frame.origin.x + xOffset + _frameXOffset; runBounds.origin.y = origins[lineIndex].y + self.frame.origin.y + _frameYOffset; runBounds.origin.y -= descent; UIImage *img = [UIImage imageNamed: [nextImage objectForKey:@"fileName"] ]; CGPathRef pathRef = CTFrameGetPath(f); //10 CGRect colRect = CGPathGetBoundingBox(pathRef); CGRect imgBounds = CGRectOffset(runBounds, colRect.origin.x - _frameXOffset - self.contentOffset.x, colRect.origin.y - _frameYOffset - self.frame.origin.y); [col.images addObject: //11 [NSArray arrayWithObjects:img, NSStringFromCGRect(imgBounds) , nil] ]; //load the next image //12 imgIndex++; if (imgIndex < [self.images count]) { nextImage = [self.images objectAtIndex: imgIndex]; imgLocation = [[nextImage objectForKey: @"location"] intValue]; } } } lineIndex++; }}

我知道這一段代碼非常的不好看纺铭,忍受一下教程一會就結(jié)束了。這是最后的沖刺階段刀疙。我們一部分一部分的看:
1. CTFrameGetLines給你返回CTLine對象的數(shù)組舶赔。
2. 得到CTFrameRef中所有CTLineRef的原點(Origin) - 簡而言之:你得到所有的文本行的左上角坐標列表。
3. 你的第一個圖像的屬性數(shù)據(jù)載入nextImage谦秧,imgLoaction是圖片在文本中的位置竟纳。
4. CTFrameGetVisibleStringRange為您提供了可見的文本范圍為您所渲染的frame - 即你得到你呈現(xiàn)目前文本的哪一部分胳嘲,那么你通過圖像數(shù)組循環(huán)闪萄,直到你找到的第一個圖像,這是在你呈現(xiàn)目前的frame迈喉。換句話說 -你快進到相關(guān)的一段文字在渲染此刻的圖片集歇。
5. 從文本中取出每一行(從CTFrame中取出CTLine)桶略。
6. 得到文本中每一行中的每一小塊(從CTLine中取出CTRun)。
7. 檢查nextImage是否在當前CTRun的范圍之內(nèi)-若然鬼悠,那么你必須去删性,并繼續(xù)渲染圖片在這個精確的點。
8. 你弄清楚CTRun的高度和寬度焕窝,通過使用CTRunGetTypographicBounds方法蹬挺。
9. 你計算出CTRun原點坐標,通過使用CTLineGetOffsetForStringIndex和其他偏移它掂。
10. 加載圖片使用給定的文件名巴帮,并得到當前列的矩形,并最終在圖片所需要的矩形來顯示虐秋。
11. 您創(chuàng)建一個NSArray使用UIImage和計算出來的frame榕茧,你將它添加到CTColumnView的圖像列表
12. 讀取下一張圖片OK,最后一個小步驟:找到JY_CTView中找到`[content setCTFrame:(id)frame]`并且在這一行下面添加如下:

[self attachImagesWithFrame:frame inColumnView:content];


現(xiàn)在你有了代碼,唯獨沒有豐富的內(nèi)容客给,不用擔心我給你準備了一些內(nèi)容用押,如下:
1. 下載、解壓并且把它導(dǎo)入到你的工程中URL:xiaoxiao靶剑。
2. 更改代碼如下

NSString* string = [NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"zombies" ofType:@"txt"] encoding:NSUTF8StringEncoding error:nil];JYMarkParser* mp = [[JYMarkParser alloc]init];NSAttributedString* attString =[mp attrStringFromMark:string];[_contentView setAttString:attString withImages:mp.images];[_contentView buildFrames];

點擊運行 只是一個最后一步蜻拨。說我們要在合理的列中的文本,使其充滿整個列的整個寬度桩引。添加下面的代碼來實現(xiàn)這一目標:

-(void)setAttString:(NSAttributedString )attString withImages:(NSArray )imgs{ self.images = imgs; CTTextAlignment alignment = kCTJustifiedTextAlignment; CTParagraphStyleSetting settings[ ] = { {kCTParagraphStyleSpecifierAlignment, sizeof(alignment), &alignment}, }; CTParagraphStyleRef paragraphStyle = CTParagraphStyleCreate(settings, sizeof(settings) / sizeof(settings[0])); NSDictionary attrDictionary = [NSDictionary dictionaryWithObjectsAndKeys: (__bridge id)paragraphStyle, (NSString)kCTParagraphStyleAttributeName, nil]; NSMutableAttributedString stringCopy = [[NSMutableAttributedString alloc] initWithAttributedString:attString]; [stringCopy addAttributes:attrDictionary range:NSMakeRange(0, [attString length])]; self.attString = (NSAttributedString)stringCopy;}


想要更多的段落樣式缎讼,請查閱文檔。從而獲取更多的段落樣式坑匠。When to use Core Text and why?現(xiàn)在血崭,你的雜志APP和CoreText已經(jīng)完成,也許你會問:“為什么我們使用CoreText而不使用UIWebView”。不要忘了UIWebView的是一個成熟的Web瀏覽器夹纫,使用它來形象化單個文本大材小用咽瓷。想象一下,你有你的10多個標簽的UI舰讹,這意味著你要消耗10個Safaris的內(nèi)存(好吧忱详,差不多,但你明白了吧)跺涤。所以匈睁,請記住:UIWebView的是一個很好的網(wǎng)頁瀏覽器桶错,當你需要的是一種高效的文本渲染引擎請使用CoreText航唆。Where To Go From Here?以下是我們在開發(fā)的上述CoreText教程完整的CoreText示例項目。如果你想要更多支持院刁,可以去了解CoreText糯钙。

看看應(yīng)用程序是否可以添加以下一功能:
1. 添加更多的標簽
2. CTRun支持更多的樣式
3. 添加更多的段落樣式
4. 添加到自動將樣式應(yīng)用到字邊界,段落退腥,句子的能力
5. 啟用連字和字距 - 它實際上是很酷任岸,效果 “if”和“fi”;)因為我知道你已經(jīng)在思考如何擴大解析器引擎超出了我,包括在這個簡短的教程我對你兩條建議:
> a. 學(xué)習(xí)HTML解析,參考:HMTL-Parser
b. 或者創(chuàng)建你自己的語法解析器,做任何想知道你想出使用的OBJ - C ParseKit如果您有任何疑問狡刘,請百度
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末享潜,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子嗅蔬,更是在濱河造成了極大的恐慌剑按,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,198評論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件澜术,死亡現(xiàn)場離奇詭異艺蝴,居然都是意外死亡,警方通過查閱死者的電腦和手機鸟废,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評論 3 398
  • 文/潘曉璐 我一進店門猜敢,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人盒延,你說我怎么就攤上這事缩擂。” “怎么了兰英?”我有些...
    開封第一講書人閱讀 167,643評論 0 360
  • 文/不壞的土叔 我叫張陵撇叁,是天一觀的道長供鸠。 經(jīng)常有香客問我畦贸,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,495評論 1 296
  • 正文 為了忘掉前任薄坏,我火速辦了婚禮趋厉,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘胶坠。我一直安慰自己君账,他們只是感情好,可當我...
    茶點故事閱讀 68,502評論 6 397
  • 文/花漫 我一把揭開白布沈善。 她就那樣靜靜地躺著乡数,像睡著了一般。 火紅的嫁衣襯著肌膚如雪闻牡。 梳的紋絲不亂的頭發(fā)上净赴,一...
    開封第一講書人閱讀 52,156評論 1 308
  • 那天,我揣著相機與錄音罩润,去河邊找鬼玖翅。 笑死,一個胖子當著我的面吹牛割以,可吹牛的內(nèi)容都是我干的金度。 我是一名探鬼主播,決...
    沈念sama閱讀 40,743評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼严沥,長吁一口氣:“原來是場噩夢啊……” “哼猜极!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起消玄,我...
    開封第一講書人閱讀 39,659評論 0 276
  • 序言:老撾萬榮一對情侶失蹤魔吐,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后莱找,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體酬姆,經(jīng)...
    沈念sama閱讀 46,200評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,282評論 3 340
  • 正文 我和宋清朗相戀三年奥溺,在試婚紗的時候發(fā)現(xiàn)自己被綠了辞色。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,424評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡浮定,死狀恐怖相满,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情桦卒,我是刑警寧澤立美,帶...
    沈念sama閱讀 36,107評論 5 349
  • 正文 年R本政府宣布,位于F島的核電站方灾,受9級特大地震影響建蹄,放射性物質(zhì)發(fā)生泄漏碌更。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,789評論 3 333
  • 文/蒙蒙 一洞慎、第九天 我趴在偏房一處隱蔽的房頂上張望痛单。 院中可真熱鬧,春花似錦劲腿、人聲如沸旭绒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽挥吵。三九已至,卻和暖如春花椭,著一層夾襖步出監(jiān)牢的瞬間蔫劣,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評論 1 271
  • 我被黑心中介騙來泰國打工个从, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留脉幢,地道東北人。 一個月前我還...
    沈念sama閱讀 48,798評論 3 376
  • 正文 我出身青樓嗦锐,卻偏偏與公主長得像嫌松,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子奕污,可洞房花燭夜當晚...
    茶點故事閱讀 45,435評論 2 359

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

  • 很久以前寫的文章搬到這里來放著萎羔。iOS開發(fā)中經(jīng)常會遇到做一些文字排版的需求,文字圖片混排的需求碳默,在iOS7 以前一...
    AlienJunX閱讀 609評論 0 2
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫贾陷、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,117評論 4 61
  • 系列文章: CoreText實現(xiàn)圖文混排 CoreText實現(xiàn)圖文混排之點擊事件 CoreText實現(xiàn)圖文混排之文...
    老司機Wicky閱讀 40,183評論 221 432
  • 現(xiàn)在高三的小伙伴已經(jīng)畢業(yè)了慌洪,再過幾個月也會迎來大學(xué)的畢業(yè)季。每到畢業(yè)季的時候凑保,就連空氣里好像都散發(fā)著分離的味道冈爹,不...
    語見生活閱讀 210評論 0 0
  • 看完這本書,不知道為什么我總想寫點東西欧引,不是為了記錄什么频伤,就是純粹的想說自己五味陳雜的心情。剛讀這本書我就被慕容雪...
    農(nóng)村的留守兒童閱讀 242評論 0 0