国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

CoreText原理及使用 實現圖文混排

2019-11-06 09:52:39
字體:
來源:轉載
供稿:網友

  關于富文本的排版也是現在的一個技術點,以下是近日關于CoreText的學習記錄以及個人理解,希望能對正在學習CoreText的朋友起到幫助。

      本篇文章轉自 紫色大番薯

  1.框架坐標系

  首先讓我們先來看看CoreText坐標系和UIKit坐標系的不同  

  從圖中可看出CoreText坐標系是以左下角為坐標原點,而我們常使用的UIKit是以左上角為坐標原點,因此在CoreText中的布局完成后需要對其坐標系進行轉換,否則直接繪制出現位置反轉的鏡像情況。在通常情況下我們一般做法是直接獲取當前上下文。并將當前上下文的坐標系轉換為CoreText坐標系,再將布局好的CoreText繪制到當前上下文中即可。以下是此種方案的實現邏輯

復制代碼
    //獲取當前上下文    CGContextRef context = UIGraphicsGetCurrentContext();    //翻轉坐標系步驟    //設置當前文本矩陣    CGContextSetTextMatrix(context, CGAffineTransformIdentity);    //文本沿y軸移動    CGContextTranslateCTM(context, 0, self.bounds.size.height);    //文本翻轉成為CoreText坐標系    CGContextScaleCTM(context, 1, -1);復制代碼

  2.CoreText文本布局

  CoreText的布局同UIKit布局不太相同,CoreText中布局大體思路是確定文本繪制區域,接著得到文本實際大小(frame)。其具體步驟如下:

  1.首先要確定布局時繪制的區域,其對應的類為CG(Mutable)PathRef

   2.設置文本內容,其對應的類為NS(Mutable)AttributedString

   3.根據文本內容配置其CTFramesetterRef

   4.利用CTFramesetterRef得到CTFrame

  有了以上具體步驟那我們開始實際的代碼操作:

復制代碼
    //1.創建繪制區域,顯示的區域可以用CGMUtablePathRef生成任意的形狀    CGMutablePathRef path = CGPathCreateMutable();    CGPathAddRect(path, NULL, CGRectMake(20, 50, self.bounds.size.width - 40, self.bounds.size.height - 100));       //2.創建需要繪制的文字    NSMutableAttributedString *attString = [[NSMutableAttributedString alloc] initWithString:@"/tWhen I will learn CoreText, i think it will hard for me.But it is easy./n/tIn fact,if you bengin learn, you can know that every thing is easy when you start.you just need some knowlages"];    //3.根據AttString生成CTFramesetterRef    CTFramesetterRef frameSetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attString);    CTFrameRef frame = CTFramesetterCreateFrame(frameSetter, CFRangeMake(0, [attString length]), path, NULL);復制代碼

  2.1文本屬性設置

  此處我們使用的是NSmutableAttributedString來進行文本設置,是因為我們可以很方便的設置其屬性,以下為部分屬性設置

復制代碼
    //設置繪制的文本內容     NSMutableAttributedString *attString = [[NSMutableAttributedString alloc] initWithString:@"/tWhen I will learn CoreText, i think it will hard for me.But it is easy./n/tIn fact,if you bengin learn, you can know that every thing is easy when you start.you just need some knowlages"];    //設置文本內容的屬性    //1設置部分文字顏色    [attString addAttribute:(id)kCTForegroundColorAttributeName value:[UIColor redColor] range:NSMakeRange(0 , 27)];    //2設置部分文字字體    CGFloat fontSize = 20;    CTFontRef fontRef = CTFontCreateWithName((CFStringRef)@"ArialMT", fontSize, NULL);    [attString addAttribute:(id)kCTFontAttributeName value:(__bridge id)fontRef range:NSMakeRange(0, 27)];    //3設置斜體    CTFontRef italicFontRef = CTFontCreateWithName((CFStringRef)[UIFont italicSystemFontOfSize:20].fontName, 16, NULL);    [attString addAttribute:(id)kCTFontAttributeName value:(__bridge id)italicFontRef range:NSMakeRange(27, 9)];    //4設置下劃線    [attString addAttribute:(id)kCTUnderlineStyleAttributeName value:(id)[NSNumber numberWithInteger:kCTUnderlineStyleDouble] range:NSMakeRange(36, 10)];    //5設置下劃線顏色    [attString addAttribute:(id)kCTUnderlineColorAttributeName value:(id)[UIColor greenColor].CGColor range:NSMakeRange(36, 10)];    //6設置空心字    long number1 = 2;    CFNumberRef numRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt8Type, &number1);    [attString addAttribute:(id)kCTStrokeWidthAttributeName value:(__bridge id)numRef range:NSMakeRange(56, 10)];    //7設置字體間距    long number = 10;    CFNumberRef num = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt8Type, &number);    [attString addAttribute:(id)kCTKernAttributeName value:(__bridge id)num range:NSMakeRange(40, 10)];    //8設置行間距    CGFloat lineSpacing = 10;    const CFIndex kNumberOfSettings = 3;    CTParagraphStyleSetting theSettings[kNumberOfSettings] = {        {kCTParagraphStyleSpecifierLineSpacingAdjustment, sizeof(CGFloat), &lineSpacing},        {kCTParagraphStyleSpecifierMaximumLineHeight, sizeof(CGFloat), &lineSpacing},        {kCTParagraphStyleSpecifierMinimumLineHeight, sizeof(CGFloat), &lineSpacing}    };    CTParagraphStyleRef theParagraphRef = CTParagraphStyleCreate(theSettings, kNumberOfSettings);    [attString addAttribute:(id)kCTParagraphStyleAttributeName value:(__bridge id)theParagraphRef range:NSMakeRange(0, [attString length])];復制代碼

  2.2圖片文本內容

  圖片寬高在工程中都需要加載后才知道,而在文本繪制中需要直接留出其位置再進行繪制,所以圖片的寬高都是在數據中保存好的,此處筆者用固定值來表示其寬高。為了留出其位置我們需要用空白的字符來做占位符使用。為了知道其圖片繪制的位置(即空白占位符位置)我們需要設置代理才能夠得知圖片繪制位置。具體步驟如下:

  1.創建CTRunDelegateCallbacks 回調函數:通過回調函數來確定圖片繪制的寬高

   2.創建空白占位字符  

   3.設置CTRunDeleagte:通過代理來找到該字符串,并確定圖片繪制的原點

  下面讓我們來看看具體的實現代碼

復制代碼
#PRagma mark - CTRunDelegateCallbacks Method//此處使用的字典結構來存儲數值static CGFloat heightCallBack(void *ref) {    return [(NSNumber *)[(__bridge NSDictionary *)ref objectForKey:@"height"] floatValue];}static CGFloat descentCallBack (void *ref) {    return 0;}static CGFloat widthCallBack (void *ref) {    return [(NSNumber *)[(__bridge NSDictionary *)ref objectForKey:@"width"] floatValue];}#pragma mark - 空白占位符及代理設置    //CTRunDelegateCallBacks:用于保存指針的結構體,由CTRun delegate進行回調    CTRunDelegateCallbacks callbacks;    memset(&callbacks, 0, sizeof(CTRunDelegateCallbacks));    callbacks.version = kCTRunDelegateVersion1;    callbacks.getAscent = heightCallBack;    callbacks.getDescent = descentCallBack;    callbacks.getWidth = widthCallBack;    //圖片信息字典    NSDictionary *imgInfoDic = @{@"width":@320,@"height":@230};    //創建CTRunDelegate的代理    CTRunDelegateRef delegate = CTRunDelegateCreate(&callbacks, (__bridge void*)imgInfoDic);    //使用oxFFFC作為空白占位符    unichar objectReplacementChar = 0xFFFC;    NSString *content = [NSString stringWithCharacters:&objectReplacementChar length:1];    NSMutableAttributedString *space = [[NSMutableAttributedString alloc] initWithString:content];    //設置代理   CFAttributedStringSetAttribute((CFMutableAttributedStringRef)space, CFRangeMake(0, 1), kCTRunDelegateAttributeName, delegate);復制代碼

  在貼出獲取圖片位置代碼前,還需要補充一個理論知識,在CoreText中所有的布局都是基于行(CTLineRef)來進行的,每行都是一個CTLineRef對象,在每行當中又包含多個屬性(CTRunRef)每行的屬性可設置代理,如上面筆者就是對空白占位符這個CTRunRef設置了代理。下面為CTLineRef和CTRunRef的示意圖

  

  明白此中原理后便可以上代碼了解具體怎么實現

復制代碼
    //獲取CTLine數組    NSArray *lines = (NSArray *)CTFrameGetLines(ctframe);    NSInteger lineCount = lines.count;    CGPoint lineOrigins[lineCount];    CTFrameGetLineOrigins(ctframe, CFRangeMake(0, 0), lineOrigins);    //遍歷每一個CTline    for (NSInteger i = 0; i < lineCount; i ++) {        CTLineRef line = (__bridge CTLineRef)lines[i];        NSArray *runObjArray = (NSArray *)CTLineGetGlyphRuns(line);        //遍歷每個CTLine中的CTRun找到空白字符的delegate        for (id runObj in runObjArray) {            CTRunRef run = (__bridge CTRunRef)runObj;            NSDictionary *runAttributes = (NSDictionary *)CTRunGetAttributes(run);            CTRunDelegateRef delegate = (__bridge CTRunDelegateRef)[runAttributes valueForKey:(id)kCTRunDelegateAttributeName];            if (delegate == nil) {                continue;            }            NSDictionary *metaDic = CTRunDelegateGetRefCon(delegate);            if (![metaDic isKindOfClass:[NSDictionary class]]) {                continue;            }            //找到代理后開始計算空白字符的位置            CGRect runBounds;            CGFloat ascent;            CGFloat descent;                        runBounds.size.width = CTRunGetTypographicBounds(run, CFRangeMake(0, 0), &ascent, &descent, NULL);            runBounds.size.height = ascent + descent;            //計算在行當中的x偏移量            CGFloat xOffset = CTLineGetOffsetForStringIndex(line, CTRunGetStringRange(run).location, NULL);            runBounds.origin.x = lineOrigins[i].x + xOffset;            runBounds.origin.y = lineOrigins[i].y - descent;            //獲得ctframe的繪制區域            CGPathRef pathRef = CTFrameGetPath(ctframe);            //計算此繪制區域的范圍            CGRect colRect = CGPathGetBoundingBox(pathRef);            //計算在此區域中空白字符的位置            CGRect delegateBounds= CGRectOffset(runBounds, colRect.origin.x, colRect.origin.y);            //記錄空白字符位置            _imageRect = delegateBounds;            //返回空白字符位置            return delegateBounds;        }    }//若沒有找到對應的代理則返回空位置return CGRectZero;復制代碼

  3.繪制文本內容

  繪制文本內容相對來說就比較簡單了,只需要在2句代碼即可搞定

    //繪制文本    CTFrameDraw(frame, context);    //繪制圖像    UIImage *image = [UIImage imageNamed:@"boat.jpg"];    CGContextDrawImage(context, _imageRect, image.CGImage);

  4.總結

  到此一個基本的CoreText布局排版已完成(注意繪制文本需要在drawRect中進行)。這里放上一個demo鏈接https://github.com/PurpleSweetPotatoes/CoreText_Learn.git,demo中包含了富文本點擊事件的處理,是對《iOS開發進階》書中CoreText的示例的整理,其中的邏輯思路就不在此贅述了,在demo中有詳細的注釋,朋友們可以直接下載學習。!

  

 


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 沐川县| 若尔盖县| 筠连县| 十堰市| 长乐市| 朔州市| 南投县| 金昌市| 丹阳市| 玉田县| 镶黄旗| 大石桥市| 县级市| 宁城县| 濮阳市| 天全县| 锦屏县| 金昌市| 赣州市| 安泽县| 萝北县| 斗六市| 高安市| 双江| 韶山市| 江都市| 平武县| 昌图县| 通榆县| 广州市| 哈密市| 上蔡县| 湘阴县| 漠河县| 利辛县| 乌兰察布市| 扶风县| 文成县| 永济市| 潜江市| 富川|