我有一个NSAttributedString
,一个用于绘制整个字符串的矩形,我想得到最后一个字符的矩形,如下图所示,如何使用Core Text获取它?
答案 0 :(得分:5)
特别是使用CoreText,这是一个应该工作的函数(代码中的一些注释解释了正在发生的事情):
- (CGRect)lastCharacterRectForAttributedString:(NSAttributedString *)attributedString drawingRect:(CGRect)drawingRect
{
// Start by creating a CTFrameRef using the attributed string and rect.
CTFrameRef textFrame = NULL;
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)(attributedString));
CGPathRef drawingPath = CGPathCreateWithRect(drawingRect, NULL);
textFrame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, [attributedString length]), drawingPath, NULL);
CFRelease(framesetter);
CFRelease(drawingPath);
// Line origins can be obtained from the CTFrameRef. Get the final one.
CGPoint finalLineOrigin;
CFArrayRef lines = CTFrameGetLines(textFrame);
if (CFArrayGetCount(lines) == 0) { // Safety check
CFRelease(textFrame);
return CGRectNull;
}
const CFIndex finalLineIdx = CFArrayGetCount(lines) - 1;
CTFrameGetLineOrigins(textFrame, CFRangeMake(finalLineIdx, 1), &finalLineOrigin);
// Get the glyph runs from the final line. Get the last glyph position from the final run.
CGPoint glyphPosition;
CFArrayRef runs = CTLineGetGlyphRuns(CFArrayGetValueAtIndex(lines, finalLineIdx));
if (CFArrayGetCount(runs) == 0) { // Safety check
CFRelease(textFrame);
return CGRectNull;
}
CTRunRef finalRun = CFArrayGetValueAtIndex(runs, CFArrayGetCount(runs) - 1);
if (CTRunGetGlyphCount(finalRun) == 0) { // Safety check
CFRelease(textFrame);
return CGRectNull;
}
const CFIndex lastGlyphIdx = CTRunGetGlyphCount(finalRun) - 1;
CTRunGetPositions(finalRun, CFRangeMake(lastGlyphIdx, 1), &glyphPosition);
// The bounding box of the glyph itself is extracted from the font.
CGRect glyphBounds;
CFDictionaryRef runAttributes = CTRunGetAttributes(finalRun);
CTFontRef font = CFDictionaryGetValue(runAttributes, NSFontAttributeName);
CGGlyph glyph;
CTRunGetGlyphs(finalRun, CFRangeMake(lastGlyphIdx, 1), &glyph);
CTFontGetBoundingRectsForGlyphs(font, kCTFontDefaultOrientation, &glyph, &glyphBounds, 1);
// Option 1 - The rect you've drawn in your question isn't tight to the final character; it looks approximately the height of the line. If that's what you're after:
CGRect lineBounds = CTLineGetBoundsWithOptions(CFArrayGetValueAtIndex(lines, finalLineIdx), 0);
CGRect desiredRect = CGRectMake(
CGRectGetMinX(drawingRect) + finalLineOrigin.x + glyphPosition.x + CGRectGetMinX(glyphBounds),
CGRectGetMinY(drawingRect) + (CGRectGetHeight(drawingRect) - (finalLineOrigin.y + CGRectGetMaxY(lineBounds))),
CGRectGetWidth(glyphBounds),
CGRectGetHeight(lineBounds)
);
// Option 2 - If you want a rect that closely bounds the final character, use this:
/*
CGRect desiredRect = CGRectMake(
CGRectGetMinX(drawingRect) + finalLineOrigin.x + glyphPosition.x + CGRectGetMinX(glyphBounds),
CGRectGetMinY(drawingRect) + (CGRectGetHeight(drawingRect) - (finalLineOrigin.y + glyphPosition.y + CGRectGetMaxY(glyphBounds))),
CGRectGetWidth(glyphBounds),
CGRectGetHeight(glyphBounds)
);
*/
CFRelease(textFrame);
return desiredRect;
}