富文本繪制步驟
- 首先需要一個(gè) StringA匕垫;
- 把 StringA 轉(zhuǎn)成 attributeString雏门,并添加相關(guān)樣式泞莉;
- 生成 CTFramessetter,得到 CTFrame钾麸;
- 繪制 CTFrameDraw更振。
繪制完成后,因?yàn)槔L制只是顯示饭尝,其他的需要額外操作肯腕。
如響應(yīng)相關(guān)點(diǎn)擊事件原理:
CTFrame 包含了多個(gè) CTLine,并且可以得到每個(gè) line 的起始位置和大小钥平,計(jì)算出響應(yīng)的區(qū)域范圍乎芳,然后根據(jù)點(diǎn)擊坐標(biāo)來(lái)判斷是否在響應(yīng)區(qū)。
又如圖片顯示原理:
先用空白占位符將位置保留出來(lái)帖池,然后再添加圖片和其他奈惑。
富文本繪制需要引入框架 #import <CoreText/CoreText.h>
自定義 label,在自定義 label 中按照繪制出來(lái)的文字獲取信息(位置)睡汹。
- (void)drawRect:(CGRect)rect {
// 富文本字符串
NSMutableAttributedString *attrStr = [[NSMutableAttributedString alloc] initWithString:self.text attributes:nil];
// 添加屬性
[attrStr addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:16] range:NSMakeRange(0, self.text.length)];
NSRange sepRange = NSMakeRange(40, 5);
[attrStr addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:sepRange];
// 生成 CTFrame
CTFramesetterRef framesetterRef = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attrStr);
CGPathRef pathRef = CGPathCreateWithRect(CGRectMake(0, 0, self.frame.size.width, self.frame.size.height), &CGAffineTransformIdentity);
CTFrameRef frameRef = CTFramesetterCreateFrame(framesetterRef, CFRangeMake(0, 0), pathRef, nil);
CGContextRef contextRef = UIGraphicsGetCurrentContext();
// 調(diào)整坐標(biāo)
CGContextSetTextMatrix(contextRef, CGAffineTransformIdentity);
CGContextTranslateCTM(contextRef, 0, self.frame.size.height);
CGContextScaleCTM(contextRef, 1, -1);
// 繪制
CTFrameDraw(frameRef, contextRef);
// 獲取信息
NSArray *lineArr = (__bridge NSArray *)CTFrameGetLines(frameRef);
CGPoint pointArr[lineArr.count];
memset(pointArr, 0, sizeof(pointArr));
CTFrameGetLineOrigins(frameRef, CFRangeMake(0, 0), pointArr); // 由于坐標(biāo)系關(guān)系肴甸, 不直接通過(guò)這種方式拿行(CTLine)的起始位置
double heightAddup = 0; // Y
// CTLine 信息
for (int i = 0 ; i < lineArr.count; i++) {
CTLineRef lineRef = (__bridge CTLineRef)lineArr[i];
NSArray *runArr = (__bridge NSArray *)CTLineGetGlyphRuns(lineRef);
CGFloat ascent = 0; // 上行高度
CGFloat descent = 0; // 下行高度
CGFloat lineGap = 0; // 行間距
CTLineGetTypographicBounds(lineRef, &ascent, &descent, &lineGap);
double startX = 0;
// CTRun 信息
// 字的高度
double runHeight = ascent + descent + lineGap;
for (int j = 0; j < runArr.count; j++) {
CTRunRef runRef = (__bridge CTRunRef)runArr[j];
CFRange runRange = CTRunGetStringRange(runRef);
double runWidth = CTRunGetTypographicBounds(runRef, CFRangeMake(0, 0), 0, 0, 0);
if (runRange.location == sepRange.location && runRange.length == sepRange.length) {
NSLog(@"找到位置"); // 計(jì)算需要的位置和 rect
NSLog(@"x:%f...y:%f...w:%f...h:%f", startX, heightAddup, runWidth, runHeight);
sepRect = CGRectMake(startX, heightAddup, runWidth, runHeight);
}
startX += runWidth;
}
// 字的高度疊加
heightAddup += runHeight;
}
}
找到位置之后就可以添加點(diǎn)擊事件了。
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint point = [touch locationInView:self];
if (CGRectContainsPoint(sepRect, point)) {
NSLog(@"點(diǎn)擊了紅色的文字");
}
}
不要忘了將 label 的userInteractionEnabled
屬性設(shè)置為 YES囚巴。
或者在找到的位置上添加 button原在,然后給 button 設(shè)置事件。
含圖片的富文本
#define YYCoreTextImageWidthPro @"YYCoreTextImageWidthPro"
#define YYCoreTextImageHeightPro @"YYCoreTextImageHeightPro"
static CGFloat ctRunDelegateGetWidthCallback(void *refCon) {
NSDictionary *infoDict = (__bridge NSDictionary *)(refCon);
if ([infoDict isKindOfClass:[NSDictionary class]]) {
return [infoDict[YYCoreTextImageWidthPro] floatValue];
}
return 0;
}
static CGFloat ctRunDelegateGetAscentCallback(void *refCon) {
NSDictionary *infoDict = (__bridge NSDictionary *)(refCon);
if ([infoDict isKindOfClass:[NSDictionary class]]) {
return [infoDict[YYCoreTextImageHeightPro] floatValue];
}
return 0;
}
static CGFloat ctRunDelegateGetDescentCallback(void *refCon) {
return 0;
}
static NSMutableDictionary *argDic = nil;
@implementation YYImageLabel
{
NSInteger imageSpaceIndex;
CGRect sepRect;
UIImageView *_imageView;
}
- (void)drawRect:(CGRect)rect {
// 富文本字符串
NSMutableAttributedString *attrStr = [[NSMutableAttributedString alloc] initWithString:self.text attributes:nil];
// 添加屬性
[attrStr addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:16] range:NSMakeRange(0, self.text.length)];
// 圖片占位符
imageSpaceIndex = self.text.length;
[attrStr appendAttributedString:[self sepImageSpaceWidth:50 height:30]];
NSMutableAttributedString *textAttrStr = [[NSMutableAttributedString alloc] initWithString:@"bhiuhsdfiohsifwfd" attributes:nil];
[attrStr appendAttributedString:textAttrStr];
// 生成 CTFrame
CTFramesetterRef framesetterRef = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attrStr);
CGPathRef pathRef = CGPathCreateWithRect(CGRectMake(0, 0, self.frame.size.width, self.frame.size.height), &CGAffineTransformIdentity);
CTFrameRef frameRef = CTFramesetterCreateFrame(framesetterRef, CFRangeMake(0, 0), pathRef, nil);
CGContextRef contextRef = UIGraphicsGetCurrentContext();
// 調(diào)整坐標(biāo)
CGContextSetTextMatrix(contextRef, CGAffineTransformIdentity);
CGContextTranslateCTM(contextRef, 0, self.frame.size.height);
CGContextScaleCTM(contextRef, 1, -1);
// 繪制
CTFrameDraw(frameRef, contextRef);
// 獲取信息
NSArray *lineArr = (__bridge NSArray *)CTFrameGetLines(frameRef);
CGPoint pointArr[lineArr.count];
memset(pointArr, 0, sizeof(pointArr));
CTFrameGetLineOrigins(frameRef, CFRangeMake(0, 0), pointArr); // 由于坐標(biāo)系關(guān)系彤叉, 不直接通過(guò)這種方式拿行(CTLine)的起始位置
double heightAddup = 0; // Y
// CTLine 信息
for (int i = 0 ; i < lineArr.count; i++) {
CTLineRef lineRef = (__bridge CTLineRef)lineArr[i];
NSArray *runArr = (__bridge NSArray *)CTLineGetGlyphRuns(lineRef);
CGFloat ascent = 0; // 上行高度
CGFloat descent = 0; // 下行高度
CGFloat lineGap = 0; // 行間距
CTLineGetTypographicBounds(lineRef, &ascent, &descent, &lineGap);
double startX = 0;
// CTRun 信息
// 字的高度
double runHeight = ascent + descent + lineGap;
for (int j = 0; j < runArr.count; j++) {
CTRunRef runRef = (__bridge CTRunRef)runArr[j];
CFRange runRange = CTRunGetStringRange(runRef);
double runWidth = CTRunGetTypographicBounds(runRef, CFRangeMake(0, 0), 0, 0, 0);
if (imageSpaceIndex == runRange.location && imageSpaceIndex < runRange.location + runRange.length) {
NSLog(@"找到位置"); // 計(jì)算需要的位置和 rect
NSLog(@"x:%f...y:%f...w:%f...h:%f", startX, heightAddup, runWidth, runHeight);
sepRect = CGRectMake(startX, heightAddup, runWidth, runHeight);
}
startX += runWidth;
}
// 字的高度疊加
heightAddup += runHeight;
}
[self setNeedsLayout];
}
- (void)layoutSubviews {
if (sepRect.size.width > 0) {
if (!_imageView) {
_imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"1.png"]];
[self addSubview:_imageView];
}
[_imageView setFrame:sepRect];
}
}
- (NSMutableAttributedString *)sepImageSpaceWidth:(float)width height:(float)height {
CTRunDelegateCallbacks callbacks;
memset(&callbacks, 0, sizeof(CTRunDelegateCallbacks));
callbacks.getWidth = ctRunDelegateGetWidthCallback;
callbacks.getAscent = ctRunDelegateGetAscentCallback;
callbacks.getDescent = ctRunDelegateGetDescentCallback; // 0
callbacks.version = kCTRunDelegateVersion1;
// 創(chuàng)建占位符
NSMutableAttributedString *spaceAttrStr = [[NSMutableAttributedString alloc] initWithString:@" "];
// 參數(shù)動(dòng)態(tài)化
argDic = [NSMutableDictionary dictionary];
[argDic setValue:@(width) forKey:YYCoreTextImageWidthPro];
[argDic setValue:@(height) forKey:YYCoreTextImageHeightPro];
CTRunDelegateRef runDelegateRef = CTRunDelegateCreate(&callbacks, (__bridge void *)argDic);
// 配置占位的屬性
CFAttributedStringSetAttribute((CFMutableAttributedStringRef)spaceAttrStr, CFRangeMake(0, 1), kCTRunDelegateAttributeName, runDelegateRef);
return spaceAttrStr;
}