CTFrameGetLineOrigins Bug
There seem to be other people with this problem, but I have yet to find a solution. I am trying to create a UILabel with a highlighted background, exactly like the labels underneath desktop files in OS X, as shown:
I have decided to approach this using CoreText, and I have stumbled upon a bug I can't seem to solve:
The -drawRect Method
In my UILabel's drawRect method, I use CoreText to draw text to the screen, and then create a UIBezierPath for each line of text drawn. (The label will never have more than two lines)
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.
After the CGRect is obtained for each line, it is put into a UIBezierPath, which is then merged with other lines' path through the -appendPath
method. A layer mask is made from the path, and the label's background color is changed to blue.
The Problem
Oddly enough, this is the output of the code above:
The mask is located too far down, and I'm not sure why. It appears that the CTFrameGetLineOrigins
function is returning an incorrect y value.
By adding the following line of code:
lineFrameOrigin.y -= self.font.leading / 2;
The result is improved:
However, it still appears to be too far down on the y-axis. I believe that the above line is just a coincidence, a value that happens to be close to the correct y-value.
Anyway, as I know this question has been asked before, are there any alternative ways I can accomplish this? Has any new information appeared on this odd bug? I am dumbfounded by why the function's values are off.
I would do something like this to get a lines rect:
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);
I prefer getting typographical bounds over image bounds. I think you need the ascent, decent and leading to get the origin. I also always offset from the frames origin as well but you may not need to.
链接地址: http://www.djcxy.com/p/14380.html上一篇: 底部而不是左边