富文本

富文本_第1张图片
图一.png

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

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


富文本_第2张图片
图二.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>(
{run count = 2, string range = (0, 9), width = 165.078, A/D/L = 15.48/6.12/0.54, glyph count = 9, runs = (

{string range = (0, 3), string = "\u54C8\u54C8\u54C8", attributes = {type = mutable dict, count = 2,
entries =>
    0 : {contents = "NSColor"} = UIExtendedSRGBColorSpace 0 0 1 1
    2 : {contents = "NSFont"} = {name = .PingFangSC-Regular, size = 18.000000, matrix = 0x0, descriptor = {attributes = {type = mutable dict, count = 1,
entries =>
    2 : {contents = "NSFontNameAttribute"} = {contents = ".PingFangSC-Regular"}
}
>}}
}
}


{string range = (3, 6), string = "\uFF0C\u4F60\u662F\u505A\u68D2\u7684", attributes = {type = mutable dict, count = 1,
entries =>
    2 : {contents = "NSFont"} = {name = .PingFangSC-Regular, size = 18.000000, matrix = 0x0, descriptor = {attributes = {type = mutable dict, count = 1,
entries =>
    2 : {contents = "NSFontNameAttribute"} = {contents = ".PingFangSC-Regular"}
}
>}}
}
}

)
}
)
富文本_第3张图片
图三.png
  • 例二,给文本添加一个100宽,100长的空格.如图四所示。
#import 

#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
富文本_第4张图片
图四.png
  • 例三,选择空格的位置,将其填充为图片。如图五所示。
#import 

#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
富文本_第5张图片
图五.png

你可能感兴趣的:(富文本)