1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
| #import "ZJTextImageV.h" #import<CoreText/CoreText.h>
@interface ZJTextImageV ()
@end @implementation ZJTextImageV - (instancetype)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { self.backgroundColor = [UIColor orangeColor]; } return self; }
- (void)drawRect:(CGRect)rect{ [super drawRect:rect];
// 初始化一个富文本对象 NSMutableAttributedString * attributeStr = [[NSMutableAttributedString alloc] initWithString:@"这里在测试图文混排,\n我是一个富文本"]; // 添加文本属性 [attributeStr addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:25] range:NSMakeRange(4, 2)]; // 插入图片 // 创建一个回调结构体,设值相关参数 CTRunDelegateCallbacks callBacks; // memset将开辟内存空间初始化 memset(&callBacks, 0, sizeof(CTRunDelegateCallbacks)); // 设置回调版本 callBacks.version = kCTRunDelegateVersion1; // 设置图片顶部距离基线的距离 callBacks.getAscent = ascentCallBacks; // 设置图片底部距离基线的距离 callBacks.getDescent = descentCallBacks; // 设置图片的宽度 callBacks.getWidth = widthCallBacks; // 创建一个代理 NSDictionary * dicPic = @{@"height":@50,@"width":@100}; CTRunDelegateRef delegate = CTRunDelegateCreate(&callBacks,(__bridge void *)dicPic); // 占位符 unichar placeHolder = 0xFFFC; NSString * placeHolderStr = [NSString stringWithCharacters:&placeHolder length:1]; // 占位富文本 NSMutableAttributedString * placeHolderAttriStr = [[NSMutableAttributedString alloc] initWithString:placeHolderStr]; // 给占位字符串设置代理 CFAttributedStringSetAttribute((CFMutableAttributedStringRef)placeHolderAttriStr, CFRangeMake(0, 1), kCTRunDelegateAttributeName, delegate); CFRelease(delegate); // 插入占位字符 [attributeStr insertAttributedString:placeHolderAttriStr atIndex:15]; // 设置行间距 CGFloat lineSpacing = 30; const CFIndex num = 1; CTParagraphStyleSetting settings[num] = {{kCTParagraphStyleSpecifierLineSpacingAdjustment, sizeof(CGFloat), &lineSpacing}};//数组 CTParagraphStyleRef paragraphRef = CTParagraphStyleCreate(settings, num); [attributeStr addAttribute:NSParagraphStyleAttributeName value:(__bridge id)(paragraphRef) range:NSMakeRange(0, attributeStr.length)]; // 释放 CFRelease(paragraphRef); // 绘制文本 // 一个frame工厂,负责生产frame CTFramesetterRef frameSetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attributeStr); // 创建绘制区域 CGMutablePathRef path = CGPathCreateMutable(); // 添加绘制尺寸 CGPathAddRect(path, NULL, self.bounds); // 富文本长度 NSInteger length = attributeStr.length; // 工厂根据绘制区域及富文本(可选范围,多次设置)设置frame CTFrameRef frame = CTFramesetterCreateFrame(frameSetter, CFRangeMake(0, length), path, NULL); // 获取当前绘制上下文 CGContextRef context = UIGraphicsGetCurrentContext(); // 设值字形的变换矩阵为不做变换 CGContextSetTextMatrix(context, CGAffineTransformIdentity); // 平移方法,将画布向上平移一个视图高度 CGContextTranslateCTM(context, 0, self.bounds.size.height); // 缩放方法,x轴缩放系数为1,则不变,y轴缩放系数为-1,则相当于以x轴为轴旋转180度 CGContextScaleCTM(context, 1, -1); // 整个区域绘制 CTFrameDraw(frame, context); // 绘制图片 UIImage * image = [UIImage imageNamed:@"icon"]; CGRect imgR = [self calculateImageRectWithFrame:frame]; CGContextDrawImage(context,imgR, image.CGImage); // 释放 CFRelease(frameSetter); CFRelease(path); CFRelease(frame); }
static CGFloat ascentCallBacks(void * ref) { return [(NSNumber *)[(__bridge NSDictionary *)ref valueForKey:@"height"] floatValue]; } static CGFloat descentCallBacks(void * ref) { return 0; } static CGFloat widthCallBacks(void * ref) { return [(NSNumber *)[(__bridge NSDictionary *)ref valueForKey:@"width"] floatValue]; }
-(CGRect)calculateImageRectWithFrame:(CTFrameRef)frame { // 根据frame获取需要绘制的线的数组 NSArray * arrLines = (NSArray *)CTFrameGetLines(frame); // 获取线的数量 NSInteger count = [arrLines count]; // 建立点的数组 CGPoint points[count]; // 获取起点 CTFrameGetLineOrigins(frame, CFRangeMake(0, 0), points); // 遍历线的数组 for (int i = 0; i < count; i ++) { // 获取线对象 CTLineRef line = (__bridge CTLineRef)arrLines[i]; // 获取run数组 NSArray * arrGlyphRun = (NSArray *)CTLineGetGlyphRuns(line); // 遍历run数组 for (int j = 0; j < arrGlyphRun.count; j ++) { // 获取run对象 CTRunRef run = (__bridge CTRunRef)arrGlyphRun[j]; // 获取run属性 NSDictionary * attributes = (NSDictionary *)CTRunGetAttributes(run); // 获取代理 CTRunDelegateRef delegate = (__bridge CTRunDelegateRef)[attributes valueForKey:(id)kCTRunDelegateAttributeName]; if (delegate == nil) { // 非空 continue; } // 判断代理字典 NSDictionary * dic = CTRunDelegateGetRefCon(delegate); if (![dic isKindOfClass:[NSDictionary class]]) { // 非空 continue; } CGPoint point = points[i]; // 获取一个起点 CGFloat ascent; // 上距 CGFloat descent; // 下距 CGRect boundsRun; // frame // 获取宽 boundsRun.size.width = CTRunGetTypographicBounds(run, CFRangeMake(0, 0), &ascent, &descent, NULL); // 获取高 boundsRun.size.height = ascent + descent; // 获取x偏移量 CGFloat xOffset = CTLineGetOffsetForStringIndex(line, CTRunGetStringRange(run).location, NULL); // point是行起点位置,加上每个字的偏移量得到每个字的x boundsRun.origin.x = point.x + xOffset; // 计算原点 boundsRun.origin.y = point.y - descent; // 获取绘制区域 CGPathRef path = CTFrameGetPath(frame); // 获取剪裁区域边框 CGRect colRect = CGPathGetBoundingBox(path); CGRect imageBounds = CGRectOffset(boundsRun, colRect.origin.x, colRect.origin.y); return imageBounds; } } return CGRectZero; }
|