美文网首页
Coretext 图文混排 一个 view的实现

Coretext 图文混排 一个 view的实现

作者: 我的大名叫小爱 | 来源:发表于2016-08-07 13:26 被阅读185次

    一个老司机的 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

    相关文章

      网友评论

          本文标题:Coretext 图文混排 一个 view的实现

          本文链接:https://www.haomeiwen.com/subject/jidlsttx.html