1. CoreText 中CTRunGetPosition
函数的小坑
下面代码为例
7283612d-a27a-45e9-b769-1b7d513ad3f5-134466.jpg
- for循环每次执行到第二个循环的时候,肯定会在NSlog 那里挂掉?
因为CTRunGetPosition
中第二个参数range,如果range.length == 0
的话,是去获取当前CTRun
里面每个字形的position
,所有第三个参数需要传递的是一个数组。 -
CTRunGetPosition
这个函数返回的字形的position
是相对当前其所在的CTLine
的,position.y === 0 position.x = 到line左边的距离
。
2. UIView的 drawRect:
方法
看下面简单的代码
- (void)drawRect:(CGRect)rect{
CGContextRef ctx = UIGraphicsGetCurrentContext();
int s = random() % 2;
if (s == 1) {
CGContextSaveGState(ctx);
CGPathRef path = CGPathCreateWithRect(CGRectMake(10, 40, 50, 30), nil);
CGContextAddPath(ctx, path);
CGContextSetFillColorWithColor(ctx, [UIColor redColor].CGColor);
CGContextFillPath(ctx);
CGContextRestoreGState(ctx);
}else{
CGContextSaveGState(ctx);
CGPathRef path = CGPathCreateWithRect(CGRectMake(100, 100, 50, 30), nil);
CGContextAddPath(ctx, path);
CGContextSetFillColorWithColor(ctx, [UIColor redColor].CGColor);
CGContextFillPath(ctx);
CGContextRestoreGState(ctx);
}
}
当我们想让 UIView
去重新绘制界面的时候,会去 调用 setNeedsDisplay
方法,然后系统会去调用 drawRect
绘制,这种情况下回清空以前的内容,重新开始绘制; 但是当调用 setNeedsDisplayInRect:
的时候 并且rect
不等于 UIVIew
的bounds
的时候,不会清空以前的内容,并且在传入的rect
内重新绘制。
3.CoreText 中frame代表什么 段落间距到底如何设置分析?
NSMutableAttributedString *atrriStr = [[NSMutableAttributedString alloc] initWithString:@"一二三四五六七八九十一二三四五六七八九十一二三四567890\n123五六七八九十一二三四五六七八九十一二三四五六七八九十gh"];
CTFramesetterRef frameSetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)atrriStr);
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddRect(path, NULL,rect);
int offset = 28;
CTFrameRef frame = CTFramesetterCreateFrame(frameSetter, CFRangeMake(0, offset), path,nil);
CTFrameRef frame2 = CTFramesetterCreateFrame(frameSetter, CFRangeMake(offset, atrriStr.length - offset), path, nil);
上面的代码 生成的结果如下
26233274-8B83-430B-BFFD-3F428AD9D3F8.png
可以看到90竟然跑到上面去了,可以总结出:
1. 一个长的string 生成多个frame 然后每个frame 都调用CTFrameDraw(frame, ctx);去绘制的时候每个frame都是从容器的原点开始绘制的,并不会去应用段落间距样式,CoreText内部还是以换行符为段落分隔符,并不是每个CTFrame就是一个段落。
4. CoreText关于内存释放的问题
@interface TextLine : NSObject
+ (instancetype)lineWithCTLine:(CTLineRef)ctline position:(CGPoint)position;
@property (nonatomic,readonly) CTLineRef ctLine;
@end
/**
处理line 里面的attachment
*/
- (void)proceeAttachment{
if (!_ctLine) {
return;
}
self.runRectAry = [NSMutableArray array];
self.runStrRangeAry = [NSMutableArray array];
// 遍历line里面的runs,看看那个run有runDelegate,有就表示这个run是图片,然后保存起来
CFArrayRef runs = CTLineGetGlyphRuns(_ctLine);
NSInteger runCount = CFArrayGetCount(runs);
for (int i = 0; i < runCount; i++) {
CTRunRef run = CFArrayGetValueAtIndex(runs, i);
CFRange strRange = CTRunGetStringRange(run);
NSValue *rangeValue = [NSValue valueWithRange:NSMakeRange(strRange.location, strRange.length)];
[self.runStrRangeAry addObject:rangeValue];
// --- 下面这段代码把每个run的rect保存起来
CFIndex glyphCount = CTRunGetGlyphCount(run);
if (glyphCount == 0) {
[self.runRectAry addObject:[NSValue valueWithCGRect:CGRectZero]];
}else{
CGPoint runPos;
CTRunGetPositions(run, CFRangeMake(0, 1), &runPos);
CGFloat ascent;
CGFloat descent;
CGFloat leading;
CGFloat runWidth;
runWidth = CTRunGetTypographicBounds(run, CFRangeMake(0, 0), &ascent, &descent, &leading);
CGRect rect = CGRectMake(runPos.x, runPos.y, runWidth, ascent + descent);
NSValue *v = [NSValue valueWithCGRect:rect];
if (v) {
[self.runRectAry addObject:v];
}
}
// ---
CFDictionaryRef runAttri = CTRunGetAttributes(run);
CTRunDelegateRef runDelegate = CFDictionaryGetValue(runAttri, kCTRunDelegateAttributeName);
// FuTextRun *attachment = (__bridge FuTextRun *)CTRunDelegateGetRefCon(runDelegate);
GWWTextAttachmentCallbackModel *model = (__bridge GWWTextAttachmentCallbackModel *)CTRunDelegateGetRefCon(runDelegate);
if (!model || model.type == GWWTextAttachmentTypeBlack) {
continue;
}
CGPoint runPoint; // 这个point 是相对于当前line里面计算的
CTRunGetPositions(run, CFRangeMake(0, 0), &runPoint);
// 计算附件的位置信息,如果附件图片比当前行小,就居中,大就底部对齐
GWWTextAttachment *txtAttach = [[GWWTextAttachment alloc] init];
txtAttach.srcUrl = model.originalString;
txtAttach.rect = (CGRect){runPoint,model.size};
txtAttach.position = runPoint;
CFRange range = CTRunGetStringRange(run);
txtAttach.rangeOfOriginString = NSMakeRange(range.location, range.length);
// 谨记 这个不能释放,因为runAttri 取到的数据是包含在run里面的导致以后释放line的时候遇到
// [CFDictionary release]: message sent to deallocated instance 0x1c806fa80
// 问题
// CFRelease(runAttri);
// CFRelease(runDelegate);
[self.attachmentAry addObject:txtAttach];
}
}
- (void)dealloc{
// 对象释放的时候要记得释放ctline
if (_ctLine) {
CFRelease(_ctLine);
}
}
还有设置attributeString的属性样式的时候也要记得release
CTParagraphStyleRef paragraphStyle = CTParagraphStyleCreate(setting, sizeof(setting) / sizeof(CTParagraphStyleSetting));
[attributes setObject:(__bridge id)paragraphStyle forKey:(id)kCTParagraphStyleAttributeName];
CFRelease(paragraphStyle);
CTRunDelegateRef runDelegate = CTRunDelegateCreate(&callbacks, (__bridge_retained void *)model);
[string addAttributes:@{(id)kCTRunDelegateAttributeName: (__bridge id)runDelegate} range:NSMakeRange(0, string.length)];
CFRelease(runDelegate);
para泄漏 2.png
网友评论