Implementation and Application of the principle of photo-text



CTFrame canvas as a whole, wherein a line (CTLine), each row can be divided into one or more small block (ctrun), the same points on the properties of the characters in a small box.

Because the drawing only shows, other additional operations required, such as in response to click events related principles: CTFrame comprising a plurality CTLine, and you can get regional response to the start position of each line was calculated out, and then you click according It coordinates to determine whether the response area. Another example picture shows the principle: first with blank placeholders to stay out of the position, and then add pictures

Rich text drawing

Rich text drawing steps:

  1. First need a StringA
  2. StringA convert into attributeString, and add the relevant style
  3. Generating CTFramesetter, to give CTFrame
  4. Draw 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);
}

Add an event

To add an event

  1. CTFrame traversal CTLine, CTLine traversal CTRun
  2. Position obtaining CTRun determines whether it is within the scope of CFRange find the designated location and startY startX
  3. The x and y to find out the position of its composition rect
  4. Click the location to determine whether the event in rect, if a trigger event

    - (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(@"点击");
        }
    }
    

Photo-text

Photo-text step

  1. Defined attributeString, inside placeholders and attributes width Higher
  2. According attributeString draw
  3. Traversing the frame, line, run to find out the coordinates and size of placeholders
  4. Call [self setNeedsLayout] automatically after layoutSubviews, set the location and pictures 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;
}

Original: big column  photo-text realization of the principle and application


Guess you like

Origin www.cnblogs.com/chinatrump/p/11614899.html