Coretext 图文混排 一个 view的实现

一个老司机的 Coretext图文混排 非常容易理解

简书:老司机Wicky  可以点击查看.

//注:对上面那位老司机的 demo 代码一行一行的注释 供自己以后学习参考.感谢  老司机Wicky


#import "tuwenview.h"

#import@implementation tuwenview

// Only override drawRect: if you perform custom drawing.

// An empty implementation adversely affects performance during animation.

- (void)drawRect:(CGRect)rect {

// Drawing code

[super drawRect:rect];

//获取到上下文

CGContextRef context = UIGraphicsGetCurrentContext();

//实现翻转

CGContextSetTextMatrix(context, CGAffineTransformIdentity);

CGContextTranslateCTM(context, 0, self.bounds.size.height);

CGContextScaleCTM(context, 1.0, -1.0);

//属性字符串

NSMutableAttributedString *attributestr = [[NSMutableAttributedString alloc]initWithString:@"\\n这里在测试图文混排,\\n我是一个富文本"];

//为图片设置CTRunDelegate,delegate决定留给图片的空间大小

CTRunDelegateCallbacks callbacks;

memset(&callbacks, 0, sizeof(CTRunDelegateCallbacks));

callbacks.version = kCTRunDelegateVersion1;

callbacks.getAscent = ascentCallBacks;

callbacks.getDescent = descentCallBacks;

callbacks.getWidth =  widthCallBacks;

NSDictionary *dicpic = @{@"height":@129,@"width":@400};

//通过以上创建了图片显示大小的代理

CTRunDelegateRef delegate = CTRunDelegateCreate(&callbacks, (__bridge void * )(dicpic));

//不能直接显示图片而是要在原来的属性字符创中插入一个占位符

unichar placeholderchar = 0xfffc;

NSString *placeholderStr = [NSString stringWithCharacters:&placeholderchar length:1];

//将占位字符串转化为属性字符串

NSMutableAttributedString * placeHolderAttrStr = [[NSMutableAttributedString alloc] initWithString:placeholderStr];

//不太明白这里是什么意思??????

CFAttributedStringSetAttribute((CFMutableAttributedStringRef)placeHolderAttrStr, CFRangeMake(0, 1), kCTRunDelegateAttributeName, delegate);

//这个 delegate 使用完毕 释放内存 已经存储在placeHolderAttrStr中了

CFRelease(delegate);

//将这个属性字符串插入到需要混排的属性字符串中间

[attributestr insertAttributedString:placeHolderAttrStr atIndex:8];

//下面是获取到 ctframe 的套路

CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attributestr);

//定义一个需要将混排后的显示区域

CGMutablePathRef path = CGPathCreateMutable();

//设置混排的区域为从从当前视图的左上角开始的矩形区域

CGPathAddRect(path, NULL, self.bounds);

//需要排列的属性文本的大小长度  不同长度 排出来的 frame 大小不一样

NSInteger length = attributestr.length;

//通过前面的长度回去 frame  (其中最后一个参数的用来定制这个 ctframe)

CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, length), path, NULL);

//混排

CTFrameDraw(frame, context);

//拿到需要排列的图片

UIImage *image = [UIImage imageNamed:@"bg.png"];

//从上面混排的frame 计算实际 image 的 frame  这个计算大小的方式自定义

CGRect imgframerect = [self calculateImageRectWithFrame:frame];

CGContextDrawImage(context, imgframerect, image.CGImage);

//手动释放内存

CFRelease(frame);

CFRelease(path);

CFRelease(framesetter);

//关闭上下文 忘了怎么写

}

//为了代理获取到这个图片占位符的实际大小使用的几个 C 函数

static CGFloat ascentCallBacks (void * ref){

return [(NSNumber *)[(__bridge NSDictionary *)ref valueForKey:@"height"] floatValue];

}

//获取到 descent

static CGFloat descentCallBacks(void * ref)

{

return 0;

}

//获取到 width

static CGFloat widthCallBacks(void * ref)

{

return [(NSNumber *)[(__bridge NSDictionary *)ref valueForKey:@"width"] floatValue];

}

//如何通过全部的计算得到图片的 frame 的方法  定制化

- (CGRect)calculateImageRectWithFrame:(CTFrameRef) rect{

//获取到绘制区域的所有 ctlines

NSArray *arraylines = (NSArray *)CTFrameGetLines(rect);

//获取到 lines 的数量

NSInteger count = [arraylines count];

CGPoint points[count];

//构建一个数组每一个 ctrun的起始点 将诶一个 ctlines 的初始点保存在 points 中间

CTFrameGetLineOrigins(rect, CFRangeMake(0, 0), points);

for(int i = 0 ; i < count ;i++){

//循环拿到每一行 lines

CTLineRef line = (__bridge CTLineRef)arraylines[i];

//获取到每一个行中的所有的 ctrun

NSArray *ctruns = (NSArray *)CTLineGetGlyphRuns(line);

//使用 for 循环遍历该行的所有的 ctrun

for(int j = 0; j < ctruns.count; j++){

CTRunRef run = (__bridge CTRunRef)ctruns[j];

NSDictionary *attributes = (NSDictionary *)CTRunGetAttributes(run);

//获取到该 run 的代理

CTRunDelegateRef delegate = (__bridge CTRunDelegateRef)[attributes valueForKey:(id)kCTRunDelegateAttributeName];

//如果代理为空 进入到下一个循环

if(delegate == nil){

continue;

}

NSDictionary *dic = CTRunDelegateGetRefCon(delegate);

//如果有代理 但是代理属性不是 前面赋值的字典类型的 继续下一个循环

if(![dic isKindOfClass:[NSDictionary class]]){

continue;

}

//到这里说明是找到自己的代理了 类型为 字典类型的

//将这个ctrun 的起点保存起来

CGPoint point = points[i];

//上

CGFloat ascent;

//下

CGFloat  descent;

CGRect boundsRun;

//获取到宽 (这个宽度为紧贴的宽度)

boundsRun.size.width = CTRunGetTypographicBounds(run, CFRangeMake(0, 0), &ascent, &descent, NULL);

//已经有值了 可以计算出高度

boundsRun.size.height = ascent + descent;

//从当前的行中获取到该 run 的偏移量  这个类似于固定值 应该可以改变

CGFloat xoffset = CTLineGetOffsetForStringIndex(line, CTRunGetStringRange(run).location, NULL);

//设置 origh 的x y

boundsRun.origin.x = point.x + xoffset;

boundsRun.origin.y = point.y - descent;

//绘到屏幕 拿到当前的 ctframe的 path

CGPathRef path = CTFrameGetPath(rect);

CGRect colRect = CGPathGetBoundingBox(path);

//校正 rect

CGRect imageBounds = CGRectOffset(boundsRun, colRect.origin.x, colRect.origin.y);

//返回img的 rect

return imageBounds;

}

}

return CGRectZero;;

}

@end

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,012评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,589评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,819评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,652评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,954评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,381评论 1 210
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,687评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,404评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,082评论 1 238
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,355评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,880评论 1 255
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,249评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,864评论 3 232
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,007评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,760评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,394评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,281评论 2 259

推荐阅读更多精彩内容

  • 最近在网上看了一些大牛的文章,自己也试着写了一下,感觉图文混排真的很强大。 废话不多说,开始整 先上效果图跟代码,...
    AllureJM阅读 946评论 0 1
  • CoreText是iOS/OSX中文本显示的一个底层框架,它是用C语言写成的,有快速简单的优势。iOS中的Text...
    小猫仔阅读 4,817评论 2 9
  • 本章前言 在上一篇《基于 CoreText 的排版引擎:基础》中,我们学会了排版的基础知识,现在我们来增加复杂性,...
    赤色追风阅读 859评论 0 2
  • iOS没有现成的支持图文混排的控件,而要用多个基础控件组合拼成图文混排这样复杂的排版,是件很苦逼的事情。对此的解决...
    清风沐沐阅读 644评论 0 2
  • blog.csdn.net CoreText实现图文混排 - 博客频道 CoreText实现图文混排 也好久没来写...
    K_Gopher阅读 563评论 0 0