码迷,mamicode.com
首页 > 其他好文 > 详细

图文混排原理实现及应用

时间:2019-10-01 12:04:22      阅读:245      评论:0      收藏:0      [点我收藏+]

标签:getc   image   uiimage   ade   判断   draw   height   gre   def   

技术图片
技术图片
技术图片

CTFrame作为一个整体的画布,其中由行(CTLine)组成,每行可以分为一个或多个小方块(CTRun),属性一样的字符就分在一个小方块里。

因为绘制只是显示,其他需要的额外操作,如响应相关点击事件原理:CTFrame包含了多个CTLine,并且可以得到每个line的起始位置与大小,计算出你响应的区域范围,然后根据你点击的坐标来判断是否在响应区。再如图片显示原理:先用空白占位符来把位置留出来,然后再添加图片

富文本绘制

富文本绘制步骤:

  1. 先需要一个StringA
  2. 把StringA转换成attributeString,并添加相关样式
  3. 生成CTFramesetter,得到CTFrame
  4. 绘制CTFrameDraw
@interface EOCTextLabel() {
    NSRange sepRange;
    CGRect sepRect;
}
@end

@implementation EOCTextLabel
- (void)drawRect:(CGRect)rect {
    sepRange = NSMakeRange(30, 5);
    NSMutableAttributedString *attriStr = [[NSMutableAttributedString alloc] initWithString:self.text attributes:nil];
    [attriStr addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:16] range:NSMakeRange(0, self.text.length)];
    [attriStr addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:sepRange];
    CGContextRef context = UIGraphicsGetCurrentContext();
    //生成frame
    CTFramesetterRef frameset = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attriStr);
    CGPathRef path = CGPathCreateWithRect(CGRectMake(0, 0, self.frame.size.width, self.frame.size.height), &CGAffineTransformIdentity);
    CTFrameRef frame = CTFramesetterCreateFrame(frameset, CFRangeMake(0, 0), path, nil);
    //调整坐标
    CGContextSetTextMatrix(context, CGAffineTransformIdentity);
    CGContextTranslateCTM(context, 0, self.frame.size.height);
    CGContextScaleCTM(context, 1, -1);
    //绘制
    CTFrameDraw(frame, context);
}

事件添加

事件添加步骤

  1. CTFrame中遍历CTLine,CTLine中遍历CTRun
  2. 获得CTRun的位置判断其是否在CFRange范围内,寻找出指定位置的startX和startY
  3. 根据寻找出的x和y组成其位置rect
  4. 判断事件的点击位置是否处于rect中,如果是则触发事件

    - (void)drawRect:(CGRect)rect {
        //...绘制
        //获取信息
        NSArray *lines = (__bridge NSArray *)CTFrameGetLines(frame);
        CGPoint pointArray[lines.count];
        memset(pointArray, 0, sizeof(pointArray));
        //由于坐标系的关系,不直接通过这种方式拿行(CTLine)的起始位置
        CTFrameGetLineOrigins(frame, CFRangeMake(0, 0), pointArray);
        double heightAddup = 0;
        //CTLine信息获取
        for (int i=0; i<lines.count; i++) {
            CTLineRef line = (__bridge CTLineRef)lines[i];
            NSArray *runs = (__bridge NSArray *)CTLineGetGlyphRuns(line);
            CGFloat ascent = 0;
            CGFloat descent = 0;
            CGFloat lineGap = 0;
            CTLineGetTypographicBounds(line, &ascent, &descent, &lineGap);
            double runHeight = ascent + descent + lineGap;
            double startX = 0;
            //CTRun信息获取
            for (int j=0; j<runs.count; j++) {
                CTRunRef run = (__bridge CTRunRef)runs[j];
                CFRange runRange = CTRunGetStringRange(run);
                double runWidth = CTRunGetTypographicBounds(run, CFRangeMake(0, 0), 0, 0, 0);
                if (runRange.location == sepRange.location && runRange.length == sepRange.length) {
                    //计算我们需要的位置和size,即rect
                    NSLog(@"找到了");
                    NSLog(@"%f, %f, %f, %f", startX, heightAddup, runWidth, runHeight);
                    sepRect = CGRectMake(startX, heightAddup, runWidth, runHeight);
                    //只有点击第三个字符时才会触发
                    //sepRect = CGRectMake(startX+runWidth*2/5, heightAddup, runWidth/5, runHeight);
                }
                startX += runWidth;
            }
            //字的高度叠加
            heightAddup += runHeight;
            NSLog(@"%f====%f", pointArray[i].y, heightAddup);
        }
        //添加button按钮和事件也可以达到需求要求
        [self setNeedsLayout];
    }
    
    - (void)layoutSubviews {
        if (sepRect.size.width>0) {
        }
    }
    
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
        UITouch *touch = [touches anyObject];
        CGPoint point = [touch locationInView:self];
        if (CGRectContainsPoint(sepRect, point)) {
            NSLog(@"点击");
        }
    }
    

图文混排

图文混排步骤

  1. 定义attributeString,里边包含占位符及其宽高等属性
  2. 根据attributeString绘制
  3. 遍历frame,line,run找出占位符的坐标及大小
  4. 调用[self setNeedsLayout]后自动layoutSubviews,设置ImageView的位置和图片

    #define EOCCoreTextImageWidthPro    @"EOCCoreTextImageWidthPro"
    #define EOCCoreTextImageHeightPro   @"EOCCoreTextImageHeightPro"
    
    static CGFloat ctRunDelegateGetWidthCallback(void *refCon) {
        NSDictionary *infoDic = (__bridge NSDictionary *)refCon;
        if ([infoDic isKindOfClass:[NSDictionary class]]) {
            return [infoDic[EOCCoreTextImageWidthPro] floatValue];
        }
        return 0;
    }
    static CGFloat ctRunDelegateGetAscentCallback(void *refCon) {
        NSDictionary *infoDic = (__bridge NSDictionary *)refCon;
        if ([infoDic isKindOfClass:[NSDictionary class]]) {
            return [infoDic[EOCCoreTextImageHeightPro] floatValue];
        }
        return 0;
    }
    static CGFloat ctRunDelegateGetDescentCallback(void *refCon) {
        return 0;
    }
    
    @interface EOCImageLabel() {
        NSInteger ImageSpaceIndex;
        CGRect sepRect;
        UIImageView *_eocImageV;
    }
    @end
    
    @implementation EOCImageLabel
    
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
    ImageSpaceIndex = self.text.length;
    NSMutableAttributedString *attriStr = [[NSMutableAttributedString alloc] initWithString:self.text attributes:nil];
    [attriStr addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:16] range:NSMakeRange(0, self.text.length)];
    //图片占位符添加
    NSMutableAttributedString *attriImageSpaceStr = [self sepImageSpaceWithWidth:100 height:50];
    [attriStr appendAttributedString:attriImageSpaceStr];
    NSMutableAttributedString *attriTailStr = [[NSMutableAttributedString alloc] initWithString:@"123456789" attributes:nil];
    [attriStr appendAttributedString:attriTailStr];
    CGContextRef context = UIGraphicsGetCurrentContext();
    //生成frame
    CTFramesetterRef frameset = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attriStr);
    CGPathRef path = CGPathCreateWithRect(CGRectMake(0, 0, self.frame.size.width, self.frame.size.height), &CGAffineTransformIdentity);
    CTFrameRef frame = CTFramesetterCreateFrame(frameset, CFRangeMake(0, 0), path, nil);
    //调整坐标
    CGContextSetTextMatrix(context, CGAffineTransformIdentity);
    CGContextTranslateCTM(context, 0, self.frame.size.height);
    CGContextScaleCTM(context, 1, -1);
    //绘制
    CTFrameDraw(frame, context);
    //获取信息
    NSArray *lines = (__bridge NSArray *)CTFrameGetLines(frame);
    CGPoint pointArray[lines.count];
    memset(pointArray, 0, sizeof(pointArray));
    //由于坐标系的关系,不直接通过这种方式拿行(CTLine)的起始位置
    CTFrameGetLineOrigins(frame, CFRangeMake(0, 0), pointArray);
    double heightAddup = 0;
    //CTLine信息获取
    for (int i=0; i<lines.count; i++) {
        CTLineRef line = (__bridge CTLineRef)lines[i];
        NSArray *runs = (__bridge NSArray *)CTLineGetGlyphRuns(line);
        CGFloat ascent = 0;
        CGFloat descent = 0;
        CGFloat lineGap = 0;
        CTLineGetTypographicBounds(line, &ascent, &descent, &lineGap);
        double runHeight = ascent + descent + lineGap;
        double startX = 0;
        //CTRun信息获取
        for (int j=0; j<runs.count; j++) {
            CTRunRef run = (__bridge CTRunRef)runs[j];
            CFRange runRange = CTRunGetStringRange(run);
            double runWidth = CTRunGetTypographicBounds(run, CFRangeMake(0, 0), 0, 0, 0);
            if (ImageSpaceIndex==runRange.location && ImageSpaceIndex < runRange.location + runRange.length) {
                //计算我们需要的位置和size,即rect
                NSLog(@"找到了");
                NSLog(@"%f, %f, %f, %f", startX, heightAddup, runWidth, runHeight);
                sepRect = CGRectMake(startX, heightAddup, runWidth, runHeight);
                //只有点击第三个字符时才会触发
                //sepRect = CGRectMake(startX+runWidth*2/5, heightAddup, runWidth/5, runHeight);
            }
            startX += runWidth;
        }
        //字的高度叠加
        heightAddup += runHeight;
        NSLog(@"%f====%f", pointArray[i].y, heightAddup);
    }
    //添加button按钮和事件也可以达到需求要求
    [self setNeedsLayout];
}

- (void)layoutSubviews {
    if (sepRect.size.width>0) {
        _eocImageV = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"0.png"]];
        [self addSubview:_eocImageV];
    }
    _eocImageV.frame = sepRect;
}

- (NSMutableAttributedString *)sepImageSpaceWithWidth:(float)width height:(float)height {
    //创建占位符
    NSMutableAttributedString *spaceAttribut = [[NSMutableAttributedString alloc] initWithString:@" " attributes:nil];
    //配置占位符的属性
    CTRunDelegateCallbacks callbacks;
    memset(&callbacks, 0, sizeof(CTRunDelegateCallbacks));
    callbacks.getAscent = ctRunDelegateGetAscentCallback;
    callbacks.getDescent = ctRunDelegateGetDescentCallback;
    callbacks.getWidth = ctRunDelegateGetWidthCallback;
    callbacks.version = kCTRunDelegateCurrentVersion;
    static NSMutableDictionary *argDic = nil;
    argDic = [[NSMutableDictionary alloc] init];
    [argDic setValue:@(width) forKey:EOCCoreTextImageWidthPro];
    [argDic setValue:@(height) forKey:EOCCoreTextImageHeightPro];
    CTRunDelegateRef runDelegate = CTRunDelegateCreate(&callbacks, (__bridge void*)argDic);
    CFAttributedStringSetAttribute((CFMutableAttributedStringRef)spaceAttribut, CFRangeMake(0, 1), kCTRunDelegateAttributeName, runDelegate);
    return spaceAttribut;
}

原文:大专栏  图文混排原理实现及应用


图文混排原理实现及应用

标签:getc   image   uiimage   ade   判断   draw   height   gre   def   

原文地址:https://www.cnblogs.com/petewell/p/11614895.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!