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.