CoreText图文混排思路以及代码实现

图文混排

CoreText实际上并没有相应API直接将一个图片转换为CTRun并进行绘制,它所能做的只是为图片预留相应的空白区域,而真正的绘制则是交由CoreGraphics完成。(像OSX就方便很多,直接将图片打包进NSTextAttachment即可,根本无须操心绘制的事情,所以基于这个想法,M80AttributedLabel的接口和实现也是使用了attachment这么个概念,图片或者UIView都是被当作文字段中的attachment。)

在CoreText中提供了CTRunDelegate这么个Core Foundation类,顾名思义它可以对CTRun进行拓展:AttributedString某个段设置kCTRunDelegateAttributeName属性之后,CoreText使用它生成CTRun是通过当前Delegate的回调来获取自己的ascent,descent和width,而不是根据字体信息。这样就给我们留下了可操作的空间:用一个空白字符作为图片的占位符,设好Delegate,占好位置,然后用CoreGraphics进行图片的绘制。以下就是整个图文混排代码描述的过程:

    // 自定检查图片,并处理图片相关信息 -> 存放图片数据模型的数组
    self.imageArr = [[self.attributedString setImageWithImageSize:self.imageSize font:self.font] mutableCopy];


/**
 * 绘制图片
 */
- (void)drawImages
{
    // 如果_frameRef不存在,直接退出
    if (!self.frameRef) return;
    
    // 移除以前的图片视图
    [self.subviews enumerateObjectsUsingBlock:^(__kindof UIView * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        [obj removeFromSuperview];
    }];
    
    // 1.获取需要展示的行数
    // 1.1获取lineRef的数组
    CFArrayRef lines = CTFrameGetLines(self.frameRef);
    // 1.2获取lineRef的个数
    CFIndex lineCount = CFArrayGetCount(lines);
    // 1.3计算需要展示的行树
    NSUInteger numberOfLines = self.numberOfLines != 0 ? MIN(lineCount, self.numberOfLines) : lineCount;
    
    //  2.获取每一行的起始位置数组
    CGPoint lineOrigins[numberOfLines];
    CTFrameGetLineOrigins(self.frameRef, CFRangeMake(0, numberOfLines), lineOrigins);
    
    // 3.循环遍历每一组中是否包含link
    for (CFIndex idx = 0; idx < numberOfLines; idx ++) {
        // 3.1寻找图片占位符的准备工作
        // 3.1.1获取idx对应行的lineRef
        CTLineRef lineRef = CFArrayGetValueAtIndex(lines, idx);
        // 3.1.2获取当前lineRef中的runRef数组
        CFArrayRef runs = CTLineGetGlyphRuns(lineRef);
        // 3.1.3获取当前lineRef中的runRef的个数
        CFIndex runCount = CFArrayGetCount(runs);
        // 3.1.4获取每一行对应的位置
        CGPoint lineOrigin = lineOrigins[idx];
        
        // 3.2遍历lineRef中的runRef,查找图片占位符
        for (CFIndex idx = 0; idx < runCount; idx ++) {
            // 3.2.1获取lineRef中对应的RunRef
            CTRunRef runRef = CFArrayGetValueAtIndex(runs, idx);
            // 3.2.2获取对应runRef的属性字典
            NSDictionary *runAttributes = (NSDictionary *)CTRunGetAttributes(runRef);
            // 3.2.3获取对应runRef的CTRunDelegateRef
            CTRunDelegateRef delegate = (__bridge CTRunDelegateRef)[runAttributes valueForKey:(id)kCTRunDelegateAttributeName];
            // 3.2.3如果不存在,直接退出本次遍历
            // ->证明不是图片,因为我们只给图片设置了CTRunDelegateRef
            if (nil == delegate) continue;
            
            // ->证明图片在runRef里
            // 4.开始绘制图片
            // 4.1获取图片的数据模型
            ZYAttributedImage *imageData = (ZYAttributedImage *)CTRunDelegateGetRefCon(delegate);
            
            // 4.2获取需要展示图片的frame
            CGRect imageFrame = CTRunGetTypographicBoundsForImageRect(runRef, lineRef, lineOrigin, imageData);
            
            // 4.3添加图片
            if (imageData.imageType == SXTImageGIFTppe) {
                // 初始化imageView
                UIImageView *imageView = [UIImageView imageViewWithGIFName:imageData.imageName frame:imageFrame];
                // 调整imageView的Y坐标
                [imageView setY:self.height - imageView.height - imageView.y];
                [self addSubview:imageView];
            }else{
                // 添加图形上下文
                CGContextRef context = UIGraphicsGetCurrentContext();
                UIImage *image = [UIImage imageNamed:imageData.imageName];
                // 绘制图片
                CGContextDrawImage(context, imageFrame, image.CGImage);
            }
        }
    }
}
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 系列文章: CoreText实现图文混排 CoreText实现图文混排之点击事件 CoreText实现图文混排之文...
    老司机Wicky阅读 40,314评论 221 432
  • 文字排版的基础概念 字体(Font):和我们平时说的字体不同,计算机意义上的字体表示的是同一大小,同一样式(Sty...
    iOS白水阅读 683评论 0 0
  • 0. 基本知识准备 0.1 字形( Glyph)基本了解 基础原点(Origin)首先是位于基线上处于基线最左侧的...
    破弓阅读 3,198评论 4 24
  • 最近在网上看了一些大牛的文章,自己也试着写了一下,感觉图文混排真的很强大。 废话不多说,开始整 先上效果图跟代码,...
    AllureJM阅读 1,025评论 0 1
  • 自从认识了无戒 我陷入不能自拔 时时刻刻的关注 一发而不可收拾 朝思日想夜难寐 不是码字就看字 除了输出便输入 短...
    王大生阅读 341评论 2 9