首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在NSLayoutManager中使用boundingRectForGlyphRange计算单词边界时,如何消除前导空格

在NSLayoutManager中使用boundingRectForGlyphRange计算单词边界时,如何消除前导空格
EN

Stack Overflow用户
提问于 2014-02-10 06:56:14
回答 2查看 1.1K关注 0票数 2

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

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

代码语言:javascript
复制
 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似乎对我的目的非常方便。

EN

回答 2

Stack Overflow用户

发布于 2014-05-22 23:00:11

要忽略左边距,请使用:

代码语言:javascript
复制
textContainer.lineFragmentPadding = 0;
票数 2
EN

Stack Overflow用户

发布于 2014-02-10 09:13:04

我不能强迫NSLayoutManager省略左边距。如果有人知道如何让NSLayoutManager忽略左边距,请告诉我。

我的变通方法是使用Core Text。这要困难得多,也复杂得多。我的解决方案不适合粘贴到单个代码摘录中,但如果你想走同样的路,这应该会给你一个很好的参考:

代码语言:javascript
复制
- (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‘变量指的是在上面的代码示例中计算的线原点):

代码语言:javascript
复制
    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

上面是一个粗略的摘录-I分解CTLine,在初始化器中计算一次行的边界,然后在获得一个单词的帧时只计算左+右端点。

呼!

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/21666258

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档