最近在做实时聊天,出现了滚动tableview卡顿问题,经过研究发现是因为图片太多造城的,于是试着用coretext在label的drawrect方法里面绘制果然取得了不错的效果,下面跟大家分享一下。
1.绘制文字
我是自定义了一个label,在label
里面进行图文混排 该文件要包含,在之前一定要在工程里添加coreText.framework
#import<CoreText/CoreText.h>
首先在label的drawrect方法里面
CGContextRef context =UIGraphicsGetCurrentContext();//获取绘制上下文
CGContextSetShadowWithColor(context,CGSizeMake(0.3,0.3), 0.3, [UIColorcolorWithRed:0green:0blue:0alpha:0.6].CGColor);//设置文本阴影
CGContextSetTextMatrix(context,CGAffineTransformIdentity);//翻转当前的坐标系(因为对于底层绘制引擎来说,屏幕左下角为(0,0))
CGAffineTransform flipVertical =CGAffineTransformMake(1,0,0,-1,0,self.bounds.size.height);
CGContextConcatCTM(context, flipVertical);//将当前context的坐标系进行反转
下面创建一个富文本string
NSMutableAttributedString* usernameattribute = [[NSMutableAttributedStringalloc]initWithString:@"coreTextTest"];
2.添加图片占位
如果里面需要添加图片那么先要预留下一个空白位置给图片,最后在统一绘制图片
NSString* imageName =kNew_UserId2_living;//图片名字
CTRunDelegateCallbacks imageCallbacks;//CTRunDelegateCallbacks 来给图片设置一个占位
imageCallbacks.version =kCTRunDelegateVersion1;
imageCallbacks.dealloc =RunDelegateDeallocCallback;
imageCallbacks.getAscent =RunDelegateGetAscentCallback;//获取图片高
imageCallbacks.getDescent =RunDelegateGetDescentCallback;//获取图片离底部距离
imageCallbacks.getWidth =RunDelegateGetWidthCallback;//获取图片宽度
CTRunDelegateRef runDelegate =CTRunDelegateCreate(&imageCallbacks, (__bridgevoid*)imageName);
NSMutableAttributedString *attribute = [[NSMutableAttributedStringalloc] initWithString:@" "];//空格用于给图片留位置
[attribute addAttribute:(NSString *)kCTRunDelegateAttributeNamevalue:(__bridgeid)runDelegate range:NSMakeRange(0,1)];
CFRelease(runDelegate);
[attribute addAttribute:@"imageName"value:imageName range:NSMakeRange(0,1)];
NSMutableAttributedString* username = [[NSMutableAttributedString alloc]initWithAttributedString:attribute];
[usernameattribute appendAttributedString:username];
void RunDelegateDeallocCallback(void* refCon){
}
CGFloat RunDelegateGetAscentCallback(void* refCon){
NSString *imageName = (__bridge NSString*)refCon;
return [UIImage imageNamed:imageName].size.height;
}
CGFloat RunDelegateGetDescentCallback(void *refCon){
return 0;
}
CGFloat RunDelegateGetWidthCallback(void *refCon){
NSString *imageName = (__bridge NSString *)refCon;
return [UIImage imageNamed:imageName].size.width;
}
这四个方法写在drawrect方法外面
//创建文本, 行间距
CGFloat lineSpace = 0.3;
CTParagraphStyleSetting lineSpaceStyle;
lineSpaceStyle.spec = kCTParagraphStyleSpecifierLineSpacing;
lineSpaceStyle.valueSize = sizeof(lineSpace);
lineSpaceStyle.value=&lineSpace;
//换行模式
CTParagraphStyleSetting lineBreakMode;
CTLineBreakMode lineBreak = kCTLineBreakByCharWrapping;
lineBreakMode.spec = kCTParagraphStyleSpecifierLineBreakMode;
lineBreakMode.value = &lineBreak;
lineBreakMode.valueSize = sizeof(CTLineBreakMode);
//设置 段落间距
CGFloat paragraph = 1.0;
CTParagraphStyleSetting paragraphStyle;
paragraphStyle.spec = kCTParagraphStyleSpecifierParagraphSpacing;
paragraphStyle.valueSize = sizeof(CGFloat);
paragraphStyle.value = ¶graph;
CTParagraphStyleSetting settings[] = {
lineBreakMode,lineSpaceStyle,paragraph
};
CTParagraphStyleRef style = CTParagraphStyleCreate(settings, sizeof(settings));
NSMutableDictionary *attributes = [NSMutableDictionary dictionaryWithObject:(id)style forKey:(id)kCTParagraphStyleAttributeName ];
// set attributes to attributed string
[usernameattribute addAttributes:attributes range:NSMakeRange(0, [usernameattribute length])];
//创建绘制区域
CGMutablePathRef path = CGPathCreateMutable();
CGRect bounds = CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height);
CGPathAddRect(path, NULL, bounds);
//根据AttributedString生成CTFramesetterRef
CTFramesetterRef frameSetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)usernameattribute);
CTFrameRef frame = CTFramesetterCreateFrame(frameSetter, CFRangeMake(0, [usernameattribute length]), path, NULL);
//进行绘制
CTFrameDraw(frame, context);
CFArrayRef lines = CTFrameGetLines(frame);
CGPoint lineOrigins[CFArrayGetCount(lines)];
CTFrameGetLineOrigins(frame, CFRangeMake(0, 0), lineOrigins);
for (CFIndex i = 0; i < CFArrayGetCount(lines); i++) {
CTLineRef line = (CTLineRef)CFArrayGetValueAtIndex(lines, i);
CGFloat lineAscent;
CGFloat lineDescent;
CGFloat lineLeading;
CTLineGetTypographicBounds(line, &lineAscent, &lineDescent, &lineLeading);
CFArrayRef runs = CTLineGetGlyphRuns(line);
for (CFIndex j = 0; j < CFArrayGetCount(runs); j++) {
CGFloat runAscent;
CGFloat runDescent;
CGPoint lineOrigin = lineOrigins[i];
CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(runs, j);
NSDictionary* attributes = (NSDictionary*)CTRunGetAttributes(run);
CGRect runRect;
runRect.size.width = CTRunGetTypographicBounds(run, CFRangeMake(0,0), &runAscent, &runDescent, NULL);
runRect=CGRectMake(lineOrigin.x + CTLineGetOffsetForStringIndex(line, CTRunGetStringRange(run).location, NULL), lineOrigin.y - runDescent, runRect.size.width, runAscent + runDescent);
NSString *imageName = [attributes objectForKey:@"imageName"];
//图片渲染逻辑
UIImage *image = [UIImage imageNamed:imageName];
if (image) {
CGRect imageDrawRect;
imageDrawRect.size = image.size;
imageDrawRect.origin.x = runRect.origin.x + lineOrigin.x;
imageDrawRect.origin.y = lineOrigin.y-2;
CGContextDrawImage(context, imageDrawRect, image.CGImage);
}
}
}
CFRelease(frame);
CFRelease(path);
CFRelease(frameSetter);
这样定制出来的cell即使有图片滑动也非常顺滑
下面就是计算cell的这一行的高度了
计算富文本高度用一下方法
- (int)getAttributedStringHeightWithString:(NSAttributedString *) string WidthValue:(int) width
{
int total_height = 0;
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)string); //string 为要计算高度的NSAttributedString
CGRect drawingRect = CGRectMake(0, 0, width, 1000); //这里的高要设置足够大
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddRect(path, NULL, drawingRect);
CTFrameRef textFrame = CTFramesetterCreateFrame(framesetter,CFRangeMake(0,0), path, NULL);
CGPathRelease(path);
CFRelease(framesetter);
NSArray *linesArray = (NSArray *) CTFrameGetLines(textFrame);
CGPoint origins[[linesArray count]];
CTFrameGetLineOrigins(textFrame, CFRangeMake(0, 0), origins);
int line_y = (int) origins[[linesArray count] -1].y; //最后一行line的原点y坐标
CGFloat ascent;
CGFloat descent;
CGFloat leading;
CTLineRef line = (__bridge CTLineRef) [linesArray objectAtIndex:[linesArray count]-1];
CTLineGetTypographicBounds(line, &ascent, &descent, &leading);
total_height = 1000 - line_y + (int) descent +1; //+1为了纠正descent转换成int小数点后舍去的值
CFRelease(textFrame);
return total_height;
}