当前位置: 首页>编程语言>正文

Coretext 图文混排 一个 view的实现

一个老司机的 Coretext图文混排 非常容易理解

简书:老司机Wicky  可以点击查看.

//注:对上面那位老司机的 demo 代码一行一行的注释 供自己以后学习参考.感谢  老司机Wicky

#import "tuwenview.h"

#import@implementation tuwenview

// Only override drawRect: if you perform custom drawing.

// An empty implementation adversely affects performance during animation.

- (void)drawRect:(CGRect)rect {

// Drawing code

[super drawRect:rect];

//获取到上下文

CGContextRef context = UIGraphicsGetCurrentContext();

//实现翻转

CGContextSetTextMatrix(context, CGAffineTransformIdentity);

CGContextTranslateCTM(context, 0, self.bounds.size.height);

CGContextScaleCTM(context, 1.0, -1.0);

//属性字符串

NSMutableAttributedString *attributestr = [[NSMutableAttributedString alloc]initWithString:@"\\n这里在测试图文混排,\\n我是一个富文本"];

//为图片设置CTRunDelegate,delegate决定留给图片的空间大小

CTRunDelegateCallbacks callbacks;

memset(&callbacks, 0, sizeof(CTRunDelegateCallbacks));

callbacks.version = kCTRunDelegateVersion1;

callbacks.getAscent = ascentCallBacks;

callbacks.getDescent = descentCallBacks;

callbacks.getWidth =  widthCallBacks;

NSDictionary *dicpic = @{@"height":@129,@"width":@400};

//通过以上创建了图片显示大小的代理

CTRunDelegateRef delegate = CTRunDelegateCreate(&callbacks, (__bridge void * )(dicpic));

//不能直接显示图片而是要在原来的属性字符创中插入一个占位符

unichar placeholderchar = 0xfffc;

NSString *placeholderStr = [NSString stringWithCharacters:&placeholderchar length:1];

//将占位字符串转化为属性字符串

NSMutableAttributedString * placeHolderAttrStr = [[NSMutableAttributedString alloc] initWithString:placeholderStr];

//不太明白这里是什么意思

CFAttributedStringSetAttribute((CFMutableAttributedStringRef)placeHolderAttrStr, CFRangeMake(0, 1), kCTRunDelegateAttributeName, delegate);

//这个 delegate 使用完毕 释放内存 已经存储在placeHolderAttrStr中了

CFRelease(delegate);

//将这个属性字符串插入到需要混排的属性字符串中间

[attributestr insertAttributedString:placeHolderAttrStr atIndex:8];

//下面是获取到 ctframe 的套路

CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attributestr);

//定义一个需要将混排后的显示区域

CGMutablePathRef path = CGPathCreateMutable();

//设置混排的区域为从从当前视图的左上角开始的矩形区域

CGPathAddRect(path, NULL, self.bounds);

//需要排列的属性文本的大小长度  不同长度 排出来的 frame 大小不一样

NSInteger length = attributestr.length;

//通过前面的长度回去 frame  (其中最后一个参数的用来定制这个 ctframe)

CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, length), path, NULL);

//混排

CTFrameDraw(frame, context);

//拿到需要排列的图片

UIImage *image = [UIImage imageNamed:@"bg.png"];

//从上面混排的frame 计算实际 image 的 frame  这个计算大小的方式自定义

CGRect imgframerect = [self calculateImageRectWithFrame:frame];

CGContextDrawImage(context, imgframerect, image.CGImage);

//手动释放内存

CFRelease(frame);

CFRelease(path);

CFRelease(framesetter);

//关闭上下文 忘了怎么写

}

//为了代理获取到这个图片占位符的实际大小使用的几个 C 函数

static CGFloat ascentCallBacks (void * ref){

return [(NSNumber *)[(__bridge NSDictionary *)ref valueForKey:@"height"] floatValue];

}

//获取到 descent

static CGFloat descentCallBacks(void * ref)

{

return 0;

}

//获取到 width

static CGFloat widthCallBacks(void * ref)

{

return [(NSNumber *)[(__bridge NSDictionary *)ref valueForKey:@"width"] floatValue];

}

//如何通过全部的计算得到图片的 frame 的方法  定制化

- (CGRect)calculateImageRectWithFrame:(CTFrameRef) rect{

//获取到绘制区域的所有 ctlines

NSArray *arraylines = (NSArray *)CTFrameGetLines(rect);

//获取到 lines 的数量

NSInteger count = [arraylines count];

CGPoint points[count];

//构建一个数组每一个 ctrun的起始点 将诶一个 ctlines 的初始点保存在 points 中间

CTFrameGetLineOrigins(rect, CFRangeMake(0, 0), points);

for(int i = 0 ; i < count ;i++){

//循环拿到每一行 lines

CTLineRef line = (__bridge CTLineRef)arraylines[i];

//获取到每一个行中的所有的 ctrun

NSArray *ctruns = (NSArray *)CTLineGetGlyphRuns(line);

//使用 for 循环遍历该行的所有的 ctrun

for(int j = 0; j < ctruns.count; j++){

CTRunRef run = (__bridge CTRunRef)ctruns[j];

NSDictionary *attributes = (NSDictionary *)CTRunGetAttributes(run);

//获取到该 run 的代理

CTRunDelegateRef delegate = (__bridge CTRunDelegateRef)[attributes valueForKey:(id)kCTRunDelegateAttributeName];

//如果代理为空 进入到下一个循环

if(delegate == nil){

continue;

}

NSDictionary *dic = CTRunDelegateGetRefCon(delegate);

//如果有代理 但是代理属性不是 前面赋值的字典类型的 继续下一个循环

if(![dic isKindOfClass:[NSDictionary class]]){

continue;

}

//到这里说明是找到自己的代理了 类型为 字典类型的

//将这个ctrun 的起点保存起来

CGPoint point = points[i];

//上

CGFloat ascent;

//下

CGFloat  descent;

CGRect boundsRun;

//获取到宽 (这个宽度为紧贴的宽度)

boundsRun.size.width = CTRunGetTypographicBounds(run, CFRangeMake(0, 0), &ascent, &descent, NULL);

//已经有值了 可以计算出高度

boundsRun.size.height = ascent + descent;

//从当前的行中获取到该 run 的偏移量  这个类似于固定值 应该可以改变

CGFloat xoffset = CTLineGetOffsetForStringIndex(line, CTRunGetStringRange(run).location, NULL);

//设置 origh 的x y

boundsRun.origin.x = point.x + xoffset;

boundsRun.origin.y = point.y - descent;

//绘到屏幕 拿到当前的 ctframe的 path

CGPathRef path = CTFrameGetPath(rect);

CGRect colRect = CGPathGetBoundingBox(path);

//校正 rect

CGRect imageBounds = CGRectOffset(boundsRun, colRect.origin.x, colRect.origin.y);

//返回img的 rect

return imageBounds;

}

}

return CGRectZero;;

}

@end


https://www.xamrdz.com/lan/5zq2016648.html

相关文章: