`test`
通过 CTFrameRef 渲染文本attribute样式
```
- (void)drawRect:(CGRect)rect {
if (self.frameRef == NULL) {
return;
}
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(ctx, [UIColor whiteColor].CGColor);
CGContextFillRect(ctx, rect);
CGContextSaveGState(ctx);
[self flipContextCoordinate:ctx];
CTFrameDraw(self.frameRef, ctx);
CGContextRestoreGState(ctx);
}
//Core Text是Apple的文字渲染引擎,坐标系为自然坐标系,即左下角为坐标原点,而iOS坐标原点在左上角。
//所以,在iOS上用Core Text绘制文字时,需要转换坐标系
- (void)flipContextCoordinate:(CGContextRef)ctx{
//以上下文中心旋转180
CGContextScaleCTM(ctx, 1, -1);
//向下平移视图
CGContextTranslateCTM(ctx, 0, -CGRectGetHeight(self.bounds));
}
```
备注:坐标转换可以参考这里学习一下哦
蒙版实现
一 . 单词蒙版的原理是:以文本背景颜色为分割条件进行渲染,没有背景色的不再渲染(即为默认白色),有背景颜色的以设置的蒙版色填充(如灰色),达到蒙版的效果。
默认不填背景色,再逐一为单词填充背景色(达到空格等符号无背景,并且CTRun分割)
备注:一个CTRun为CTLine中样式一致的元素,样式一致的话一行就一个CTRun, 所以分别添加背景分割为多个单词的CTRun.
```
NSMutableAttributedString *attributedText = [[NSMutableAttributedString alloc] initWithString:@" You go first. - No you go first." attributes:@{NSForegroundColorAttributeName:[UIColor whiteColor],
NSFontAttributeName: [UIFont systemFontOfSize:14.0],
NSParagraphStyleAttributeName: [self paragraphStyle]
}];
NSArray<NSString *> *subStrArray = [attributedText.string componentsSeparatedByString:@" "];
NSInteger rangeOffset = 0;
for (int i = 0; i < subStrArray.count; i++) {
//空格分开的文本
NSString *textSub = subStrArray[i];
NSRange range = NSMakeRange(0, 0);
range.length = textSub.length;
rangeOffset += range.length;
if (i != 0) {
range.location += 1;
range.location = rangeOffset;
}
[attributedText addAttributes:[self publicAttributes] range:range];
}
```
根据CTFrame、CTLine和CTRun的结构生成CGPath确定的区域,然后绘制。
```
- (void)drawRect:(CGRect)rect {
if (self.frameRef == NULL) {
return;
}
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(ctx, [UIColor whiteColor].CGColor);
CGContextFillRect(ctx, rect);
CGContextSaveGState(ctx);
//极坐标转迪卡尔坐标。旋转180度再平移
[self flipContextCoordinate:ctx];
CTFrameDraw(self.frameRef, ctx);
CGContextRestoreGState(ctx);
//如果不需要蒙版效果,只是渲染文本attribute,上面就完成了
if (!self.runMasked){return;}
CFArrayRef lines = CTFrameGetLines(self.frameRef);
CFIndex lineCount = CFArrayGetCount(lines);
CGPoint origins[lineCount];
CTFrameGetLineOrigins(self.frameRef, CFRangeMake(0, 0), origins);
CGMutablePathRef maskedPath = CGPathCreateMutable();
for (int i=0; i<lineCount; i++) {
CGPoint lineOrigin = origins[i];
CTLineRef line = CFArrayGetValueAtIndex(lines, i);
CFArrayRef runs = CTLineGetGlyphRuns(line);
CFIndex runCount = CFArrayGetCount(runs);
CGFloat lineAscent, lineDescent;
CTLineGetTypographicBounds(line, &lineAscent, &lineDescent, NULL);
for (int j = 0; j<runCount; j++) {
CTRunRef runref = CFArrayGetValueAtIndex(runs, j);
CFRange runRange = CTRunGetStringRange(runref);
CFDictionaryRef runAttributeDic = CTRunGetAttributes(runref);
Boolean hasBGAtt = CFDictionaryContainsKey(runAttributeDic, NSBackgroundColorAttributeName);
if (!hasBGAtt) {
//没有背景色
continue;
}
CGFloat runDescent, leading;
double width = CTRunGetTypographicBounds(runref,CFRangeMake(0, 0), NULL, &runDescent, &leading);
CGFloat startOffSet = CTLineGetOffsetForStringIndex(line, runRange.location, NULL);
//coretext坐标从左下角开始,第一行在最上面,纵坐标最大
CGPoint runOri = lineOrigin;
runOri.x += startOffSet;
runOri.y += lineAscent;
//iOS坐标从左上开始,coretext从左下开始,y轴坐标系转化
runOri.y = CGRectGetHeight(self.bounds) - runOri.y;
CGRect runBounds =
(CGRect){.origin = runOri,
.size = {.width = width,
.height = lineAscent + lineDescent}};
CGPathAddRect(maskedPath, NULL, runBounds);
}
}
CGContextAddPath(ctx, maskedPath);
CGPathRelease(maskedPath);
CGContextSetFillColorWithColor(ctx, [UIColor lightGrayColor].CGColor);
CGContextFillPath(ctx);
}
```
其中坐标转换可以优化一下
```
- (void)drawRect:(CGRect)rect {
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(ctx, [UIColor whiteColor].CGColor);
CGContextFillRect(ctx, rect);
//极坐标转迪卡尔坐标。旋转180度再平移
[self flipContextCoordinate:ctx];
//保留坐标转换效果
CGContextSaveGState(ctx);
、、略、、
CGFloat runDescent, leading;
double width = CTRunGetTypographicBounds(runref,CFRangeMake(0, 0), NULL, &runDescent, &leading);
CGFloat startOffSet = CTLineGetOffsetForStringIndex(line, runRange.location, NULL);
CGPoint runOri = lineOrigin;
runOri.x += startOffSet;
runOri.y -= lineDescent;
CGRect runBounds = (CGRect){.origin = runOri, .size = {.width = width,
.height = lineAscent + lineDescent}};
、、略、、
}
```
二. 不处理CTRun重绘,设置单词和前景色和背景色都为蒙版颜色,达到伪装效果(哈哈哈)
注意:边角为直角,如果需要绘制圆角之类的需要使用上一种方式哦。
```
NSMutableAttributedString *attributedText = [[NSMutableAttributedString alloc] initWithString:@" You go first. - No you go first." attributes:@{NSForegroundColorAttributeName:[UIColor whiteColor],
NSFontAttributeName: [UIFont systemFontOfSize:14.0],
NSParagraphStyleAttributeName: [self paragraphStyle]
}];
NSArray<NSString *> *subStrArray = [attributedText.string componentsSeparatedByString:@" "];
NSInteger rangeOffset = 0;
for (int i = 0; i < subStrArray.count; i++) {
//空格分开的文本
NSString *textSub = subStrArray[i];
NSRange range = NSMakeRange(0, 0);
range.length = textSub.length;
rangeOffset += range.length;
if (i != 0) {
range.location += 1;
range.location = rangeOffset;
}
[attributedText addAttributes:@{NSForegroundColorAttributeName:[UIColor blueColor],
NSBackgroundColorAttributeName:[UIColor blueColor],
NSFontAttributeName: [UIFont systemFontOfSize:14.0]} range:range];
}
```