一個(gè)老司機(jī)的 Coretext圖文混排 非常容易理解
簡(jiǎn)書(shū):老司機(jī)Wicky ?可以點(diǎn)擊查看.
//注:對(duì)上面那位老司機(jī)的 demo 代碼一行一行的注釋 供自己以后學(xué)習(xí)參考.感謝 ?老司機(jī)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();
//實(shí)現(xiàn)翻轉(zhuǎn)
CGContextSetTextMatrix(context, CGAffineTransformIdentity);
CGContextTranslateCTM(context, 0, self.bounds.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
//屬性字符串
NSMutableAttributedString *attributestr = [[NSMutableAttributedString alloc]initWithString:@"\\n這里在測(cè)試圖文混排,\\n我是一個(gè)富文本"];
//為圖片設(shè)置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};
//通過(guò)以上創(chuàng)建了圖片顯示大小的代理
CTRunDelegateRef delegate = CTRunDelegateCreate(&callbacks, (__bridge void * )(dicpic));
//不能直接顯示圖片而是要在原來(lái)的屬性字符創(chuàng)中插入一個(gè)占位符
unichar placeholderchar = 0xfffc;
NSString *placeholderStr = [NSString stringWithCharacters:&placeholderchar length:1];
//將占位字符串轉(zhuǎn)化為屬性字符串
NSMutableAttributedString * placeHolderAttrStr = [[NSMutableAttributedString alloc] initWithString:placeholderStr];
//不太明白這里是什么意思??????
CFAttributedStringSetAttribute((CFMutableAttributedStringRef)placeHolderAttrStr, CFRangeMake(0, 1), kCTRunDelegateAttributeName, delegate);
//這個(gè) delegate 使用完畢 釋放內(nèi)存 已經(jīng)存儲(chǔ)在placeHolderAttrStr中了
CFRelease(delegate);
//將這個(gè)屬性字符串插入到需要混排的屬性字符串中間
[attributestr insertAttributedString:placeHolderAttrStr atIndex:8];
//下面是獲取到 ctframe 的套路
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attributestr);
//定義一個(gè)需要將混排后的顯示區(qū)域
CGMutablePathRef path = CGPathCreateMutable();
//設(shè)置混排的區(qū)域?yàn)閺膹漠?dāng)前視圖的左上角開(kāi)始的矩形區(qū)域
CGPathAddRect(path, NULL, self.bounds);
//需要排列的屬性文本的大小長(zhǎng)度 ?不同長(zhǎng)度 排出來(lái)的 frame 大小不一樣
NSInteger length = attributestr.length;
//通過(guò)前面的長(zhǎng)度回去 frame ?(其中最后一個(gè)參數(shù)的用來(lái)定制這個(gè) ctframe)
CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, length), path, NULL);
//混排
CTFrameDraw(frame, context);
//拿到需要排列的圖片
UIImage *image = [UIImage imageNamed:@"bg.png"];
//從上面混排的frame 計(jì)算實(shí)際 image 的 frame ?這個(gè)計(jì)算大小的方式自定義
CGRect imgframerect = [self calculateImageRectWithFrame:frame];
CGContextDrawImage(context, imgframerect, image.CGImage);
//手動(dòng)釋放內(nèi)存
CFRelease(frame);
CFRelease(path);
CFRelease(framesetter);
//關(guān)閉上下文 忘了怎么寫(xiě)
}
//為了代理獲取到這個(gè)圖片占位符的實(shí)際大小使用的幾個(gè) C 函數(shù)
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];
}
//如何通過(guò)全部的計(jì)算得到圖片的 frame 的方法 ?定制化
- (CGRect)calculateImageRectWithFrame:(CTFrameRef) rect{
//獲取到繪制區(qū)域的所有 ctlines
NSArray *arraylines = (NSArray *)CTFrameGetLines(rect);
//獲取到 lines 的數(shù)量
NSInteger count = [arraylines count];
CGPoint points[count];
//構(gòu)建一個(gè)數(shù)組每一個(gè) ctrun的起始點(diǎn) 將誒一個(gè) ctlines 的初始點(diǎn)保存在 points 中間
CTFrameGetLineOrigins(rect, CFRangeMake(0, 0), points);
for(int i = 0 ; i < count ;i++){
//循環(huán)拿到每一行 lines
CTLineRef line = (__bridge CTLineRef)arraylines[i];
//獲取到每一個(gè)行中的所有的 ctrun
NSArray *ctruns = (NSArray *)CTLineGetGlyphRuns(line);
//使用 for 循環(huán)遍歷該行的所有的 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];
//如果代理為空 進(jìn)入到下一個(gè)循環(huán)
if(delegate == nil){
continue;
}
NSDictionary *dic = CTRunDelegateGetRefCon(delegate);
//如果有代理 但是代理屬性不是 前面賦值的字典類型的 繼續(xù)下一個(gè)循環(huán)
if(![dic isKindOfClass:[NSDictionary class]]){
continue;
}
//到這里說(shuō)明是找到自己的代理了 類型為 字典類型的
//將這個(gè)ctrun 的起點(diǎn)保存起來(lái)
CGPoint point = points[i];
//上
CGFloat ascent;
//下
CGFloat ?descent;
CGRect boundsRun;
//獲取到寬 (這個(gè)寬度為緊貼的寬度)
boundsRun.size.width = CTRunGetTypographicBounds(run, CFRangeMake(0, 0), &ascent, &descent, NULL);
//已經(jīng)有值了 可以計(jì)算出高度
boundsRun.size.height = ascent + descent;
//從當(dāng)前的行中獲取到該 run 的偏移量 ?這個(gè)類似于固定值 應(yīng)該可以改變
CGFloat xoffset = CTLineGetOffsetForStringIndex(line, CTRunGetStringRange(run).location, NULL);
//設(shè)置 origh 的x y
boundsRun.origin.x = point.x + xoffset;
boundsRun.origin.y = point.y - descent;
//繪到屏幕 拿到當(dāng)前的 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