亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

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

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中有詳細的注釋,朋友們可以直接下載學習。!

  

 


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
97视频在线观看亚洲| 亚洲日韩中文字幕| 青青草原一区二区| xxxxxxxxx欧美| 亚洲天堂av女优| 日韩av网站在线| 欧美精品在线免费播放| 激情成人中文字幕| 国产成人一区三区| 2023亚洲男人天堂| 久久五月情影视| 国产成人精品av| 欧美激情精品在线| 91在线免费看网站| 国产一区二区黑人欧美xxxx| 久久久综合免费视频| 欧洲美女7788成人免费视频| 亚洲国产日韩欧美在线99| 日韩网站在线观看| 日韩av免费在线看| 国产精品成人久久久久| 97热精品视频官网| 久久99青青精品免费观看| 国产精品一区二区久久精品| 97色在线视频| 欧美成人在线影院| 97超碰蝌蚪网人人做人人爽| 亚洲第一男人天堂| 国产欧美精品一区二区三区-老狼| 国产精品第1页| 成人网欧美在线视频| 欧美第一黄网免费网站| 亚洲欧美制服丝袜| 欧美激情免费观看| 青青久久av北条麻妃海外网| 久久国产精品久久久久久| 日韩在线高清视频| 成人xxxxx| 亚洲性xxxx| 91精品国产91久久久久久| 丝袜情趣国产精品| 欧美成人久久久| 在线成人激情黄色| 一区二区国产精品视频| 欧美精品激情视频| 国产精品久久久久7777婷婷| 亚洲一二三在线| 国产成人久久久精品一区| 久久伊人免费视频| 黄色91在线观看| 亚洲国产中文字幕久久网| 日本在线观看天堂男亚洲| 国产精品成人va在线观看| 中文字幕精品在线视频| 精品高清一区二区三区| 久久久久国产精品免费网站| 最近2019中文免费高清视频观看www99| 草民午夜欧美限制a级福利片| 国产精品一区二区三区久久久| 亚洲精品一区中文| 欧美日韩国产综合新一区| 亚洲欧美国产精品va在线观看| 另类天堂视频在线观看| 欧美极品美女视频网站在线观看免费| 日韩亚洲第一页| 国产精品av网站| 中文字幕日韩有码| 日韩av有码在线| 在线亚洲午夜片av大片| 91精品视频在线免费观看| 亚洲91av视频| 91久久在线播放| 在线观看中文字幕亚洲| 91经典在线视频| 欧美激情视频网站| 久久亚洲精品一区| 国产一区二区视频在线观看| 国外日韩电影在线观看| 成人97在线观看视频| 国产aⅴ夜夜欢一区二区三区| 国产91精品青草社区| 欧美性生交大片免网| 欧美日韩国产影院| 欧美视频国产精品| 欧美国产日韩一区二区在线观看| 亚洲综合中文字幕在线观看| 中文字幕欧美国内| 97不卡在线视频| www日韩中文字幕在线看| 欧美性极品xxxx做受| 国产精品欧美一区二区| 国产一区玩具在线观看| 最近2019年中文视频免费在线观看| 亚洲福利在线播放| 亚洲精品国产精品久久清纯直播| 中文字幕成人在线| 少妇高潮久久77777| 国产一区二区在线免费视频| 亚洲自拍小视频免费观看| 国产精品久久久久久久午夜| 91精品国产91久久久久久久久| 亚洲区一区二区| 欧美专区福利在线| 亚洲黄一区二区| 国产成人啪精品视频免费网| 国产精品mp4| 国产成人精品日本亚洲专区61| 亚洲最新在线视频| 日韩大陆欧美高清视频区| 国产成人激情视频| 亚洲成人1234| 自拍偷拍亚洲精品| 亚洲欧美日韩久久久久久| 国产亚洲精品va在线观看| 国产精品一区二区三区久久| 国产精品视频久久| 日韩中文在线视频| 亚洲精品av在线播放| 亚洲美女视频网站| 国产精品自拍网| 国模吧一区二区| 欧美一级视频免费在线观看| 欧美在线www| 第一福利永久视频精品| 欧美亚洲另类制服自拍| 日韩毛片在线观看| 色777狠狠综合秋免鲁丝| 97国产一区二区精品久久呦| 欧美福利小视频| 91色在线视频| 国产精品久久久久久久久久久新郎| 欧美极品欧美精品欧美视频| 日韩中文字在线| 亚洲激情国产精品| 欧美激情第一页xxx| 久久国产精品久久久久久| 欧美激情精品久久久久久变态| 久久久久久久一区二区三区| 国产成人精品久久亚洲高清不卡| 97视频在线看| 欧美性色xo影院| 亚洲欧美日韩爽爽影院| 国产视频亚洲视频| 亚洲欧美日韩综合| www.亚洲人.com| 亚洲自拍偷拍一区| 国产成人精品免费久久久久| 久久91精品国产91久久跳| 亚洲va国产va天堂va久久| 91在线观看免费| 懂色aⅴ精品一区二区三区蜜月| 欧美成人免费播放| 欧美大片第1页| 成人av资源在线播放| 亚洲国产精品久久久久秋霞蜜臀| 精品爽片免费看久久| 国产精品久久久久久久久久ktv| 国产一区二区丝袜高跟鞋图片| 久久久99久久精品女同性| 日韩中文字幕视频| 欧美另类精品xxxx孕妇| 日韩网站免费观看| 日韩av电影在线免费播放| 日韩av影视综合网|