2

我正在将多行字符串分解为 iOS 上的单词边界。我的解决方案以 NSLayoutManager 的 boundingRectForGlyphRange 方法为中心。它几乎可以工作,除了每个单词的矩形向右偏几个像素。换句话说 NSLayoutManager 似乎在每行添加一个前导空格/缩进,我找不到任何方法来覆盖这种行为。

我尝试使用 NSLayoutManager.usesFontLeading 以及 NSParagraphStyle.headIndent 但没有任何结果:

 NSLayoutManager* layout = [NSLayoutManager new];
layout.usesFontLeading = NO;
NSMutableParagraphStyle* paragraphStyle = [NSMutableParagraphStyle new];
paragraphStyle.headIndent = paragraphStyle.firstLineHeadIndent = 0;
NSTextStorage* textStorage = [[NSTextStorage alloc] initWithString:self attributes:@{NSFontAttributeName:font, NSParagraphStyleAttributeName:paragraphStyle}];
layout.textStorage = textStorage;
NSTextContainer* textContainer = [[NSTextContainer alloc] initWithSize:size];
[layout addTextContainer:textContainer];

// compute bounding rect for each word range
for (NSValue* wordRangeValue in wordRanges)
{
    NSRange wordRange = [wordRangeValue rangeValue];
    NSRange wordGlyphRange = [layout glyphRangeForCharacterRange:wordRange actualCharacterRange:NULL];
    CGRect wordBounds = [layout boundingRectForGlyphRange:wordGlyphRange inTextContainer:textContainer];
}

在此处输入图像描述

屏幕截图:灰色矩形代表标签边界,红色矩形代表每个标签的文本矩形和从上面的 [boundingRectForGlyphRange:] 方法计算的单词边界。请注意,计算的单词边界偏离了几个像素。

我也对计算单词边界的其他方法持开放态度,但 boundingRectForGlyphRange 似乎对我的目的非常方便。

4

2 回答 2

2

要忽略左边距,请使用:

textContainer.lineFragmentPadding = 0;
于 2014-05-22T15:00:11.023 回答
0

我无法强制 NSLayoutManager 省略左边距。如果有人知道如何让 NSLayoutManager 忽略左边距,请告诉我。

我的解决方法是改用 Core Text。这更加困难和复杂。我的解决方案不适合粘贴到单个代码摘录中,但是如果您想走相同的路线,这应该会给您一个很好的参考:

- (NSArray*) coreTextLayoutsForCharacterRanges:(NSArray*)ranges withFont:(UIFont *)font constrainedToSize:(CGSize)size{

// initialization: make frame setter
NSMutableParagraphStyle* paragraphStyle = [NSMutableParagraphStyle new];
paragraphStyle.lineBreakMode = NSLineBreakByWordWrapping; // watch out - need to specify line wrapping to use multi line layout!
paragraphStyle.minimumLineHeight = font.lineHeight; // watch out - custom fonts do not compute properly without this!
NSAttributedString* attributedString = [[NSAttributedString alloc] initWithString:self attributes:@{NSFontAttributeName:font, NSParagraphStyleAttributeName:paragraphStyle}];
CTFramesetterRef frameSetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attributedString);
CFRange wholeString = CFRangeMake(0, self.length);
CGRect bounds = CGRectMake(0, 0, size.width, size.height);
CGMutablePathRef boundsPath = CGPathCreateMutable();
CGPathAddRect(boundsPath, NULL, bounds);
CTFrameRef frame = CTFramesetterCreateFrame(frameSetter, wholeString, boundsPath, NULL);
CFRelease(boundsPath);

// extract lines
CFArrayRef lines = CTFrameGetLines(frame);
int lineCount = CFArrayGetCount(lines);
CGPoint lineOrigins[lineCount];
CTFrameGetLineOrigins(frame, CFRangeMake(0, 0), lineOrigins);

NSMutableArray* lineLayouts = [NSMutableArray arrayWithCapacity:lineCount];
CGFloat h = size.height;
for (int i = 0; i < lineCount; ++i){
    CTLineRef line = (CTLineRef)CFArrayGetValueAtIndex(lines, i);
    CGPoint lineOrigin = lineOrigins[i];  // in Core Graphics coordinates! let's convert.
    lineOrigin.y = h - lineOrigin.y;
    TextLayout* lineLayout = [[CTLineLayout alloc] initWithString:self line:line lineOrigin:lineOrigin];
    [lineLayouts addObject:lineLayout];
}

// got line layouts. now we iterate through the word ranges to find the appropriate line for each word and compute its layout using the corresponding CTLine.

另一个重要部分是如何使用 CTLine 获取一行中单词的边界矩形。我将此因素考虑到 CTLineLayout 模块中,但要点是这样的('origin' 变量是指在上面的代码示例中计算的行原点):

    CGFloat ascent = 0.0f, descent = 0.0f, leading = 0.0f;
CGFloat width = CTLineGetTypographicBounds(line, &ascent, &descent, &leading);
CGFloat top = origin.y - ascent;
CGFloat bottom = origin.y + descent;
CGRect result = CGRectMake(origin.x, top, width, bottom - top); // frame of entire line (for now)
CGFloat left = CTLineGetOffsetForStringIndex(line, stringRange.location, NULL);
CGFloat right = CTLineGetOffsetForStringIndex(line, NSMaxRange(stringRange), NULL);
result.origin.x = left;
result.size.width = right - left; // frame of target word in UIKit coordinates

上面是一个粗略的摘录 - 我在初始化程序中计算了 CTLine 的边界,然后在获取单词的框架时只计算左 + 右端点。

哇!

于 2014-02-10T01:13:03.920 回答