富文本

作者: 前年的邂逅_Jerry | 来源:发表于2018-04-07 17:38 被阅读12次
    图一.png

    如图一所示,CTFrame作为一个整体的画布,其中有行(CTLine)组成,每行可以分为一个或多个小方块(CTRun),属性一样的字符就分在一个小方块里。
    富文本绘制步骤:
    1 先需要一个StringA
    2 把StringA转成attributeString,并添加相关样式
    3 生成CTFramessetter,得到CTFrame
    4 绘制CTFrameDraw

    绘制完成后,添加其他操作,如响应相关点击事件原理:
    CTFrame包含了多个CTLine,并且可以得到每个line的起始位置与大小,计算出你响应的区域范围,然后更具你点击的坐标来判断是否在响应区。


    图二.png

    下行高度为负数值
    行高= Ascent + |Descent| + Line Gap

    一、例子

    • 例一,见如图三所示。
    @implementation MyTextLbl{
        NSString * contentStr;
    }
    - (instancetype)initWithFrame:(CGRect)frame{
        self = [super initWithFrame:frame];
        if (self) {
            contentStr = @"哈哈哈,你是做棒的";
        }
        return self;
    }
    - (void)drawRect:(CGRect)rect{
        [super drawRect:rect];
        NSMutableDictionary * infoDic = [[NSMutableDictionary alloc] init];
        [infoDic setObject:[UIFont systemFontOfSize:18] forKey:NSFontAttributeName];
        NSMutableAttributedString * attributeStr = [[NSMutableAttributedString alloc] initWithString:contentStr attributes:infoDic];
    [attributeStr addAttribute:NSForegroundColorAttributeName value:[UIColor blueColor] range:NSMakeRange(0, 3)];
        //2 绘制 CFRangeMake(0, 0) 不设置区域
        CTFramesetterRef setterRef = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attributeStr);
        CGMutablePathRef pathRef = CGPathCreateMutable();
       CGPathAddRect(pathRef, NULL, CGRectMake(0, -0, self.frame.size.width, self.frame.size.height));
        CTFrameRef frameRef = CTFramesetterCreateFrame(setterRef, CFRangeMake(0, 0), pathRef, NULL);
        CGContextRef context = UIGraphicsGetCurrentContext();
        //调整坐标
        CGContextSetTextMatrix(context, CGAffineTransformIdentity);
        //沿x轴移动了0个单位,沿y轴移动了self.bounds.size.height个单位
        CGContextTranslateCTM(context, 0, self.bounds.size.height);
        //旋转Y轴坐标
        CGContextScaleCTM(context, 1, -1.0);
        CTFrameDraw(frameRef, context);
        NSArray * lineRefAry = (__bridge NSArray *)CTFrameGetLines(frameRef);
        NSLog(@"%@",lineRefAry);
        CFRelease(frameRef);
        CFRelease(pathRef);
        CFRelease(setterRef);
    }
    
    打印结果:
    po lineRefAry
    <__NSArrayM 0x604000053c50>(
    <CTLine: 0x6040005b9440>{run count = 2, string range = (0, 9), width = 165.078, A/D/L = 15.48/6.12/0.54, glyph count = 9, runs = (
    
    <CTRun: 0x7f83a560e930>{string range = (0, 3), string = "\u54C8\u54C8\u54C8", attributes = <CFBasicHash 0x60c0002798c0 [0x10c101960]>{type = mutable dict, count = 2,
    entries =>
        0 : <CFString 0x112823a18 [0x10c101960]>{contents = "NSColor"} = UIExtendedSRGBColorSpace 0 0 1 1
        2 : <CFString 0x10cb04648 [0x10c101960]>{contents = "NSFont"} = <CTFont: 0x7f83a5413c90>{name = .PingFangSC-Regular, size = 18.000000, matrix = 0x0, descriptor = <CTFontDescriptor: 0x60c0000b81e0>{attributes = <CFBasicHash 0x60c00027abc0 [0x10c101960]>{type = mutable dict, count = 1,
    entries =>
        2 : <CFString 0x10cb0a908 [0x10c101960]>{contents = "NSFontNameAttribute"} = <CFString 0x10cb02848 [0x10c101960]>{contents = ".PingFangSC-Regular"}
    }
    >}}
    }
    }
    
    
    <CTRun: 0x7f83a560d0e0>{string range = (3, 6), string = "\uFF0C\u4F60\u662F\u505A\u68D2\u7684", attributes = <CFBasicHash 0x60c000271a40 [0x10c101960]>{type = mutable dict, count = 1,
    entries =>
        2 : <CFString 0x10cb04648 [0x10c101960]>{contents = "NSFont"} = <CTFont: 0x7f83a560e650>{name = .PingFangSC-Regular, size = 18.000000, matrix = 0x0, descriptor = <CTFontDescriptor: 0x6040000b5600>{attributes = <CFBasicHash 0x604000263b80 [0x10c101960]>{type = mutable dict, count = 1,
    entries =>
        2 : <CFString 0x10cb0a908 [0x10c101960]>{contents = "NSFontNameAttribute"} = <CFString 0x10cb02848 [0x10c101960]>{contents = ".PingFangSC-Regular"}
    }
    >}}
    }
    }
    
    )
    }
    )
    
    图三.png
    • 例二,给文本添加一个100宽,100长的空格.如图四所示。
    #import <CoreText/CoreText.h>
    
    #define ATTRIBUTE_WIDTH     @"AttributeWidth"
    #define ATTRIBUTE_HEIGHT    @"AttributeHeight"
    
    @implementation MyTextLbl{
        NSString * contentStr;
    }
    
    
    
    - (instancetype)initWithFrame:(CGRect)frame{
        self = [super initWithFrame:frame];
        if (self) {
            contentStr = @"哈哈哈,你是做棒的";
        }
        return self;
    }
    
    - (void)drawRect:(CGRect)rect{
        [super drawRect:rect];
        NSMutableDictionary * infoDic = [[NSMutableDictionary alloc] init];
        [infoDic setObject:[UIFont systemFontOfSize:18] forKey:NSFontAttributeName];
        NSMutableAttributedString * attributeStr = [[NSMutableAttributedString alloc] initWithString:contentStr attributes:infoDic];
        [attributeStr addAttribute:NSForegroundColorAttributeName value:[UIColor blueColor] range:NSMakeRange(0, 3)];
        NSMutableAttributedString * attriImageStr = [self sepcailAttributeStringWith:100 height:100];
        [attributeStr appendAttributedString:attriImageStr];
        NSMutableAttributedString *attriOther = [[NSMutableAttributedString alloc] initWithString:@"我是尾巴,哈哈"];
        [attributeStr appendAttributedString:attriOther];
        //2 绘制 CFRangeMake(0, 0) 不设置区域
        CTFramesetterRef setterRef = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attributeStr);
        CGMutablePathRef pathRef = CGPathCreateMutable();
       CGPathAddRect(pathRef, NULL, CGRectMake(0, -0, self.frame.size.width, self.frame.size.height));
        CTFrameRef frameRef = CTFramesetterCreateFrame(setterRef, CFRangeMake(0, 0), pathRef, NULL);
        CGContextRef context = UIGraphicsGetCurrentContext();
        //调整坐标
        CGContextSetTextMatrix(context, CGAffineTransformIdentity);
        //沿x轴移动了0个单位,沿y轴移动了self.bounds.size.height个单位
        CGContextTranslateCTM(context, 0, self.bounds.size.height);
        //旋转Y轴坐标
        CGContextScaleCTM(context, 1, -1.0);
        CTFrameDraw(frameRef, context);
        
        NSArray * lineRefAry = (__bridge NSArray *)CTFrameGetLines(frameRef);
        NSLog(@"%@",lineRefAry);
        CFRelease(setterRef);
        CFRelease(pathRef);
        CFRelease(frameRef);
    }
    
    - (NSMutableAttributedString *)sepcailAttributeStringWith:(float)width height:(float)height{
        CTRunDelegateCallbacks callBack;
        //清空结构体中的值
        memset(&callBack, 0, sizeof(CTRunDelegateCallbacks));
        callBack.version = kCTRunDelegateVersion1;
        //设置上行高度的回掉
        callBack.getAscent = getAscentCallback;
        //设置下行高度的回掉
        callBack.getDescent = getDescentCallback;
        //设置高度的回掉
        callBack.getWidth = getWidthCallback;
        //NSDictionary *runInfo = @{ATTRIBUTE_WIDTH : @(width),ATTRIBUTE_HEIGHT : @(height)};
        NSDictionary *runInfo = [NSDictionary dictionaryWithObjectsAndKeys:@(width), ATTRIBUTE_WIDTH, @(height), ATTRIBUTE_HEIGHT, nil];
        CTRunDelegateRef delegate = CTRunDelegateCreate(&callBack, (void *)runInfo);
        NSMutableAttributedString * attriStr  = [[NSMutableAttributedString alloc] initWithString:@" " attributes:nil];
        CFAttributedStringSetAttribute((CFMutableAttributedStringRef)attriStr, CFRangeMake(0, 1), kCTRunDelegateAttributeName, delegate);
        CFRelease(delegate);
        return attriStr;
    }
    CGFloat getAscentCallback(void * refCon ){
        
        NSDictionary *runInfo = (__bridge NSDictionary*)refCon;
        return [[runInfo objectForKey:ATTRIBUTE_HEIGHT] floatValue];
        
    }
    
    CGFloat getDescentCallback(void * refCon ){
        
        return 0;
    }
    
    CGFloat getWidthCallback(void * refCon ){
        
        NSDictionary *runInfo = (__bridge NSDictionary*)refCon;
        return [[runInfo objectForKey:ATTRIBUTE_WIDTH] floatValue];
    }
    
    @end
    
    图四.png
    • 例三,选择空格的位置,将其填充为图片。如图五所示。
    #import <CoreText/CoreText.h>
    
    #define ATTRIBUTE_WIDTH     @"AttributeWidth"
    #define ATTRIBUTE_HEIGHT    @"AttributeHeight"
    
    @implementation MyTextLbl{
        NSString * contentStr;
        NSInteger imageIndex;
        float imageX;
        float imageY;
        UIImageView *_imageV;
    }
    
    
    
    - (instancetype)initWithFrame:(CGRect)frame{
        self = [super initWithFrame:frame];
        if (self) {
            contentStr = @"哈哈哈,你是做棒的";
        }
        return self;
    }
    - (void)layoutSubviews{
        
        [super layoutSubviews];
        
        if (!_imageV) {
            _imageV = [[UIImageView alloc] init];
            _imageV.image = [UIImage imageNamed:@"1.jpg"];
            [self addSubview:_imageV];
        }
    
        [_imageV setFrame:CGRectMake(imageX, imageY, 100, 100)];
        
    }
    - (void)drawRect:(CGRect)rect{
        [super drawRect:rect];
        NSMutableDictionary * infoDic = [[NSMutableDictionary alloc] init];
        [infoDic setObject:[UIFont systemFontOfSize:18] forKey:NSFontAttributeName];
        NSMutableAttributedString * attributeStr = [[NSMutableAttributedString alloc] initWithString:contentStr attributes:infoDic];
        [attributeStr addAttribute:NSForegroundColorAttributeName value:[UIColor blueColor] range:NSMakeRange(0, 3)];
        imageIndex = attributeStr.length;
        NSMutableAttributedString * attriImageStr = [self sepcailAttributeStringWith:100 height:100];
        [attributeStr appendAttributedString:attriImageStr];
        NSMutableAttributedString *attriOther = [[NSMutableAttributedString alloc] initWithString:@"我是尾巴,哈哈"];
        [attributeStr appendAttributedString:attriOther];
        //2 绘制 CFRangeMake(0, 0) 不设置区域
        CTFramesetterRef setterRef = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attributeStr);
        CGMutablePathRef pathRef = CGPathCreateMutable();
       CGPathAddRect(pathRef, NULL, CGRectMake(0, -0, self.frame.size.width, self.frame.size.height));
        CTFrameRef frameRef = CTFramesetterCreateFrame(setterRef, CFRangeMake(0, 0), pathRef, NULL);
        CGContextRef context = UIGraphicsGetCurrentContext();
        //调整坐标
        CGContextSetTextMatrix(context, CGAffineTransformIdentity);
        //沿x轴移动了0个单位,沿y轴移动了self.bounds.size.height个单位
        CGContextTranslateCTM(context, 0, self.bounds.size.height);
        //旋转Y轴坐标
        CGContextScaleCTM(context, 1, -1.0);
        CTFrameDraw(frameRef, context);
        
        
        //xuan
        NSArray * lineRefAry = (__bridge NSArray *)CTFrameGetLines(frameRef);
        //文本的总高度
        double heightAddUp = 0;//line的总高度
        for (NSInteger i = 0; i < lineRefAry.count; i++) {
            CTLineRef lineRef = (__bridge CTLineRef)lineRefAry[i];
            //上行高度
            CGFloat ascent;
            //下行高度
            CGFloat descent;
            //行间距
            CGFloat lineGap;
            CTLineGetTypographicBounds(lineRef, &ascent, &descent, &lineGap);
            NSArray * runAry = (__bridge NSArray *)CTLineGetGlyphRuns(lineRef);
            double startX = 0;
            for (NSInteger j = 0; j < runAry.count; j ++) {
                CTRunRef runRef = (__bridge CTRunRef)runAry[j];
                CFRange runRange = CTRunGetStringRange(runRef);
                if (imageIndex >= runRange.location && imageIndex < runRange.location + runRange.length) {
                    NSLog(@"imageIndex = %lu location = %lu length = %lu",imageIndex,runRange.location,runRange.length);
                    imageY = heightAddUp;
                    imageX = startX;
                    NSDictionary *infoDict = (__bridge NSDictionary*)CTRunGetAttributes(runRef);//kCTRunDelegateAttributeName
                    CTRunDelegateRef runDelegate = (__bridge CTRunDelegateRef)[infoDict objectForKey:@"CTRunDelegate"];
                    
                    NSDictionary *frameInfo = CTRunDelegateGetRefCon(runDelegate);
                    NSLog(@"frameInfo = %@",frameInfo);
                }
                double runWidth = CTRunGetTypographicBounds(runRef, CFRangeMake(0, 0), 0, 0, 0);
                startX += runWidth;
            }
            heightAddUp += ascent + fabs(descent) + lineGap;
        }
        
        [self setNeedsLayout];
        CFRelease(setterRef);
        CFRelease(pathRef);
        CFRelease(frameRef);
    }
    
    - (NSMutableAttributedString *)sepcailAttributeStringWith:(float)width height:(float)height{
        CTRunDelegateCallbacks callBack;
        //清空结构体中的值
        memset(&callBack, 0, sizeof(CTRunDelegateCallbacks));
        callBack.version = kCTRunDelegateVersion1;
        //设置上行高度的回掉
        callBack.getAscent = getAscentCallback;
        //设置下行高度的回掉
        callBack.getDescent = getDescentCallback;
        //设置高度的回掉
        callBack.getWidth = getWidthCallback;
        //NSDictionary *runInfo = @{ATTRIBUTE_WIDTH : @(width),ATTRIBUTE_HEIGHT : @(height)};
        NSDictionary *runInfo = [NSDictionary dictionaryWithObjectsAndKeys:@(width), ATTRIBUTE_WIDTH, @(height), ATTRIBUTE_HEIGHT, nil];
        CTRunDelegateRef delegate = CTRunDelegateCreate(&callBack, (void *)runInfo);
        NSMutableAttributedString * attriStr  = [[NSMutableAttributedString alloc] initWithString:@" " attributes:nil];
        CFAttributedStringSetAttribute((CFMutableAttributedStringRef)attriStr, CFRangeMake(0, 1), kCTRunDelegateAttributeName, delegate);
        CFRelease(delegate);
        return attriStr;
    }
    CGFloat getAscentCallback(void * refCon ){
        
        NSDictionary *runInfo = (__bridge NSDictionary*)refCon;
        return [[runInfo objectForKey:ATTRIBUTE_HEIGHT] floatValue];
        
    }
    
    CGFloat getDescentCallback(void * refCon ){
        
        return 0;
    }
    
    CGFloat getWidthCallback(void * refCon ){
        
        NSDictionary *runInfo = (__bridge NSDictionary*)refCon;
        return [[runInfo objectForKey:ATTRIBUTE_WIDTH] floatValue];
    }
    
    @end
    
    图五.png

    相关文章

      网友评论

          本文标题:富文本

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