CTFrameGetLineOrigins Bug

时间:2013-12-17 04:27:17

标签: objective-c core-graphics uilabel core-text

似乎有其他人有这个问题,但我还没有找到解决方案。我正在尝试使用突出显示的背景创建一个UILabel,就像OS X中桌面文件下面的标签一样,如下所示:

OS X desktop file label

我决定使用CoreText来解决这个问题,我偶然发现了一个我似乎无法解决的错误:

-drawRect方法

在我的UILabel的drawRect方法中,我使用CoreText在屏幕上绘制文本,然后为每行绘制的文本创建一个UIBezierPath。 (标签永远不会超过两行)

CGContext context = UIGraphicsGetCurrentContext();
/* Flip coordinate plane */
CGContextSetTextMatrix(context, CGAffineTransformIdentity);
CGContextTranslateCTM(context, 0, self.bounds.size.height);
CGContextScaleCTM(context, 1.0, -1.0);

CGMutablePathRef path = CGPathCreateMutable();
CGPathAddRect(path, NULL, self.bounds);

NSAttributedString *attributedText = [self attributedText]; /* [self attributedText] returns an NSAttributedString formatted with font, linebreak, and alignment. */

CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attributedText);
CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, [attributedText length]), path, NULL);
CFArrayRef lines = CTFrameGetLines(frame);

CGPoint *lineOrigins = malloc(CFArrayGetCount(lines));
CTFrameGetLineOrigins(frame, CFRangeMake(0, 0), lineOrigins); //<------

/* Get frame for line 1 */
CTLineRef line1 = CFArrayGetValueAtIndex(lines, 0);
CGRect lineFrame = CTLineGetImageBounds(line1, context); 

CGPoint lineFrameOrigin = CGPointMake(lineOrigins[0].x, self.bounds.size.height - lineOrigins[0].y);//Coordinates flipped once again
lineFrame.origin = lineFrameOrigin;

UIBezierPath *linePath = [UIBezierPath bezierPathWithRoundedRect:lineFrame cornerRadius:0];

[self.path appendPath:linePath];
/* Repeat for line 2 */

CTFrameDraw(frame, context);//Draw the text.

在为每一行获得CGRect之后,将其放入UIBezierPath,然后通过-appendPath方法与其他行的路径合并。从路径制作图层蒙版,标签的背景颜色更改为蓝色。

问题

奇怪的是,这是上面代码的输出:

enter image description here

面具位置太远,我不知道为什么。似乎CTFrameGetLineOrigins函数返回的y值不正确。

添加以下代码行:

lineFrameOrigin.y -= self.font.leading / 2;

结果有所改善:

enter image description here

然而,它在y轴上看起来仍然太远了。我相信上面这一行只是巧合,恰好接近正确的y值。

无论如何,正如我之前所知,这个问题已被提出过,我有什么其他方法可以做到这一点吗?这个奇怪的bug有没有出现任何新信息?我为什么功能的值没有了,我感到茫然。

1 个答案:

答案 0 :(得分:2)

我会做这样的事情来得到一条直线:

CTFrameRef frame;
CFArrayRef lines = CTFrameGetLines(frame);

CFIndex numLines = CFArrayGetCount(lines);

CTLineRef line1 = (CTLineRef)CFArrayGetValueAtIndex(lines, 0);

CGPoint *origins = (CGPoint *)malloc(numLines * sizeof(CGPoint));
CTFrameGetLineOrigins(self.ctFrame, CFRangeMake(0, 0), origins);

CGPoint origin = origins[0];
CGFloat ascent, descent, leading;

CTLineGetTypographicBounds(line1, &ascent, &descent, &leading);

CFRange lineRange = CTLineGetStringRange(line1);

CGFloat lineStart = CTLineGetOffsetForStringIndex(line1, lineRange.location, NULL);
CGFloat lineEnd   = CTLineGetOffsetForStringIndex(line1, CFRangeMaxRange(lineRange), NULL);

CGRect box = CGPathGetBoundingBox(CTFrameGetPath(frame));

CGFloat lineHeight = ascent + descent + leading;

CGFloat rectX = box.origin.x + origin.x + lineStart;
CGFloat rectY = box.origin.y + origin.y - lineHeight;

CGRect rectForLine = CGRectMake( rectX, rectY, lineEnd - lineStart, lineHeight);

我更喜欢在图像边界上使用印刷边界。我认为你需要提升,体面并领先获得起源。我也总是偏离帧原点,但你可能不需要。